aboutsummaryrefslogtreecommitdiffstats
path: root/src/nip29-client.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/nip29-client.js')
-rw-r--r--src/nip29-client.js41
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 {