diff options
Diffstat (limited to 'src/nip29-client.js')
| -rw-r--r-- | src/nip29-client.js | 41 |
1 files changed, 36 insertions, 5 deletions
diff --git a/src/nip29-client.js b/src/nip29-client.js index 040733c..4c27b64 100644 --- a/src/nip29-client.js +++ b/src/nip29-client.js @@ -197,18 +197,25 @@ export function nip29Client({ scope, pubkey, identity, on, listRelays, store = m // Apply one change to the newest list the relays hold (never to a copy // we only assume is current), then publish it. async function writeList(change) { - const cur = await readList(!list).catch(() => list); + const cur = await readList(true).catch(() => list); const base = cur || { tags: [], content: '', at: 0 }; const tags = change(base.tags); if (!tags) return; const created_at = Math.max(now(), base.at + 1); const ev = await sign({ kind: LIST, created_at, tags, content: base.content || '' }); - const ok = await publishOn(await listRelays(false), ev); + // everywhere a reader looks: a phone that asks only the relays a copy + // missed would never learn of a group joined on the laptop + const ok = await publishOn(await listRelays(true), ev); if (ok) { list = { tags, content: base.content || '', at: created_at }; store.put('list', list).catch(() => {}); } } + // The list as another device changed it: read at start, again whenever the + // app comes back (resubscribe), and followed live while it's open, so a + // group joined on the laptop shows up on the phone (Adam, 2026-11-12). + let adoptedAt = 0, listSub = null; async function adoptList() { + adoptedAt = Date.now(); try { - await readList(!list); + await readList(true); const l = list; // the newest, a leave's own write included if (!l || stopped) return; for (const x of listGroups(l.tags)) { @@ -299,15 +306,39 @@ export function nip29Client({ scope, pubkey, identity, on, listRelays, store = m }).catch((e) => dlog('nip29: list write', e)); } + function adoptLive(ev) { + if (stopped || ev.pubkey !== pubkey || ev.kind !== LIST) return; + if (list && ev.created_at <= list.at) return; + list = { tags: ev.tags || [], content: ev.content || '', at: ev.created_at }; + store.put('list', list).catch(() => {}); + adoptList(); + } + async function followList() { + if (listSub || stopped) return; + const relays = await listRelays(false); + if (stopped || listSub) return; + listSub = subscribeAuthed(relays, { kinds: [LIST], authors: [pubkey], limit: 1 }, adoptLive); + } async function start() { await load(); if (stopped) return; for (const g of groups.values()) subscribe(g); emit('loaded', null); adoptList(); + followList().catch(() => {}); + } + function resubscribe() { + for (const g of groups.values()) { unsubscribe(g); subscribe(g); } + if (listSub) { try { listSub(); } catch {} listSub = null; } + followList().catch(() => {}); + if (Date.now() - adoptedAt > 30_000) adoptList(); + } + function stop() { + stopped = true; + for (const g of groups.values()) unsubscribe(g); + if (listSub) { try { listSub(); } catch {} listSub = null; } + flush(); } - function resubscribe() { for (const g of groups.values()) { unsubscribe(g); subscribe(g); } } - function stop() { stopped = true; for (const g of groups.values()) unsubscribe(g); flush(); } const no = async () => { throw new Error('not supported'); }; return { |
