a demonstration replicated social networking web app built with anproto
wiredove.net/
social
ed25519
protocols
1import { getFeedRow, parseOpenedTimestamp, upsertFeedRow } from './feed_row_cache.js'
2
3const normalizeTs = (entry) => {
4 if (!entry) { return 0 }
5 if (entry.ts) {
6 const parsed = Number.parseInt(entry.ts, 10)
7 if (Number.isFinite(parsed) && parsed > 0) { return parsed }
8 }
9 return parseOpenedTimestamp(entry.opened)
10}
11
12export class FeedStore {
13 constructor(src, options = {}) {
14 this.src = src
15 this.entries = new Map()
16 this.isActive = typeof options.isActive === 'function' ? options.isActive : () => true
17 this.since = 0
18 }
19
20 normalize(entry) {
21 if (!entry || !entry.hash) { return null }
22 const ts = normalizeTs(entry)
23 const cachedRow = entry.row || getFeedRow(entry.hash)
24 if (cachedRow) {
25 upsertFeedRow(cachedRow)
26 }
27 return {
28 hash: entry.hash,
29 opened: entry.opened || null,
30 ts,
31 row: cachedRow || null
32 }
33 }
34
35 async upsert(entry) {
36 if (!this.isActive()) { return false }
37 const normalized = this.normalize(entry)
38 if (!normalized || !normalized.ts) { return false }
39 const prev = this.entries.get(normalized.hash)
40 if (prev && prev.ts >= normalized.ts && prev.row && !normalized.row) {
41 normalized.row = prev.row
42 }
43 this.entries.set(normalized.hash, normalized)
44 this.since = Math.max(this.since, normalized.ts)
45 if (!this.isActive()) { return false }
46 await window.__feedEnqueue?.(this.src, normalized)
47 return true
48 }
49
50 async upsertMany(entries) {
51 if (!Array.isArray(entries) || !entries.length) { return 0 }
52 let count = 0
53 for (const entry of entries) {
54 if (!this.isActive()) { break }
55 const ok = await this.upsert(entry)
56 if (ok) { count += 1 }
57 }
58 return count
59 }
60
61 getSince() {
62 return this.since
63 }
64}