-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathfeed_row_cache.js
More file actions
115 lines (104 loc) · 3.05 KB
/
Copy pathfeed_row_cache.js
File metadata and controls
115 lines (104 loc) · 3.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
const CACHE_KEY = 'wiredove.feedRowCache.v1'
const MAX_ROWS = 3000
let rows = null
let persistTimer = null
const nowMs = () => Date.now()
const loadRows = () => {
if (rows) { return rows }
rows = new Map()
if (typeof localStorage === 'undefined') { return rows }
try {
const raw = localStorage.getItem(CACHE_KEY)
if (!raw) { return rows }
const parsed = JSON.parse(raw)
if (!Array.isArray(parsed)) { return rows }
for (const item of parsed) {
if (!item || typeof item.hash !== 'string' || item.hash.length !== 44) { continue }
rows.set(item.hash, item)
}
} catch (err) {
console.warn('feed row cache load failed', err)
}
return rows
}
const schedulePersist = () => {
if (persistTimer) { return }
persistTimer = setTimeout(() => {
persistTimer = null
if (typeof localStorage === 'undefined') { return }
try {
const data = Array.from(loadRows().values())
localStorage.setItem(CACHE_KEY, JSON.stringify(data))
} catch (err) {
console.warn('feed row cache persist failed', err)
}
}, 500)
}
const trimRows = () => {
const map = loadRows()
while (map.size > MAX_ROWS) {
const firstKey = map.keys().next().value
if (!firstKey) { break }
map.delete(firstKey)
}
}
export const parseOpenedTimestamp = (opened) => {
if (!opened || opened.length < 13) { return 0 }
const ts = Number.parseInt(opened.substring(0, 13), 10)
return Number.isFinite(ts) ? ts : 0
}
const summarize = (txt, maxLen = 140) => {
if (!txt || typeof txt !== 'string') { return '' }
const single = txt.replace(/\s+/g, ' ').trim()
if (single.length <= maxLen) { return single }
return single.substring(0, maxLen) + '...'
}
export const makeFeedRow = ({ hash, opened = null, author = '', contentHash = '', yaml = null, ts = 0 } = {}) => {
if (!hash || typeof hash !== 'string' || hash.length !== 44) { return null }
const openedTs = ts || parseOpenedTimestamp(opened)
const preview = yaml && yaml.body
? summarize(yaml.body)
: (yaml && yaml.bio ? summarize(yaml.bio) : '')
const name = yaml && yaml.name ? yaml.name.trim() : ''
return {
hash,
ts: openedTs || 0,
opened: opened || null,
author: author || '',
contentHash: contentHash || '',
name,
preview,
replyCount: 0,
updatedAt: nowMs()
}
}
export const upsertFeedRow = (row) => {
if (!row || !row.hash) { return false }
const map = loadRows()
const prev = map.get(row.hash) || {}
const next = {
...prev,
...row,
updatedAt: nowMs()
}
map.delete(row.hash)
map.set(row.hash, next)
trimRows()
schedulePersist()
return true
}
export const getFeedRow = (hash) => {
if (!hash || typeof hash !== 'string') { return null }
const map = loadRows()
return map.get(hash) || null
}
export const attachCachedRows = (log) => {
if (!Array.isArray(log) || !log.length) { return log || [] }
const map = loadRows()
return log.map((entry) => {
if (!entry || !entry.hash) { return entry }
const row = map.get(entry.hash)
if (!row) { return entry }
return { ...entry, row }
})
}