diff options
Diffstat (limited to 'src/features/messages.js')
| -rw-r--r-- | src/features/messages.js | 33 |
1 files changed, 26 insertions, 7 deletions
diff --git a/src/features/messages.js b/src/features/messages.js index 801ad28..490ac6f 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -491,6 +491,18 @@ export function messagesFeature(ctx) { } // what a feed or a profile lists: notes, and polls (NIP-88) const FEED_KINDS = [1, 1068]; + // A profile shows its articles (NIP-23) among the notes, as Amethyst does. + const PROFILE_KINDS = [...FEED_KINDS, 30023]; + // An edited article is a new event at the same address: keep the newest. + const newestArticles = (evs) => { + const best = new Map(); + for (const e of evs) { + if (e.kind !== 30023) continue; + const k = e.pubkey + ':' + ((e.tags.find((x) => x[0] === 'd') || [])[1] || ''); + if (!best.has(k) || best.get(k).created_at < e.created_at) best.set(k, e); + } + return evs.filter((e) => e.kind !== 30023 || best.get(e.pubkey + ':' + ((e.tags.find((x) => x[0] === 'd') || [])[1] || '')) === e); + }; const myPubkeys = () => { const pks = []; const id = hook('nostrLoginIdentity'); @@ -3488,11 +3500,18 @@ export function messagesFeature(ctx) { wallet.saveFeatureState(PROF_PAGE_CACHE, c); } catch {} } - const slimNote = (ev) => ({ + // (an article keeps its kind, its card's tags and, near enough, all of + // itself: a tap opens it to read) + const ARTICLE_TAGS = ['d', 'title', 'summary', 'image', 'published_at']; + const slimNote = (ev) => ev.kind === 30023 ? { + id: ev.id, pubkey: ev.pubkey, kind: 30023, created_at: ev.created_at, + content: String(ev.content || '').slice(0, 30000), + tags: (ev.tags || []).filter((x) => ARTICLE_TAGS.includes(x[0])), + } : { id: ev.id, pubkey: ev.pubkey, kind: 1, created_at: ev.created_at, content: String(ev.content || '').slice(0, 3000), tags: (ev.tags || []).filter((x) => x[0] === 'e' || x[0] === 'p'), - }); + }; function fetchFullProfile(pk) { if (!fullProfiles.has(pk)) { @@ -4061,7 +4080,7 @@ export function messagesFeature(ctx) { function fetchNotes(pk, c) { c.fetching = true; (async () => { - const evs = await queryOn(await notesRelays(pk), { kinds: FEED_KINDS, authors: [pk], limit: 30 }, 4500); + const evs = await queryOn(await notesRelays(pk), { kinds: PROFILE_KINDS, authors: [pk], limit: 30 }, 4500); // Merged with what's already on the page, never swapped for it: the // relays answer unevenly (primal held 2 of Joe Nakamoto's posts, ours // 18, the archives 30), and a thin answer used to erase a full page. @@ -4069,7 +4088,7 @@ export function messagesFeature(ctx) { let added = 0; for (const e of evs || []) if (!byId.has(e.id)) { byId.set(e.id, e); added++; } if (!added && c.notes.length) return; - const merged = [...byId.values()].sort((a, b) => b.created_at - a.created_at); + const merged = newestArticles([...byId.values()]).sort((a, b) => b.created_at - a.created_at); await notesReady(merged.slice(0, FEED_PAGE)); // the first screen arrives whole c.notes = merged; c.end = false; @@ -4117,12 +4136,12 @@ export function messagesFeature(ctx) { render(); try { const evs = await queryOn(await notesRelays(pk), - { kinds: FEED_KINDS, authors: [pk], limit: 30, until: oldest.created_at - 1 }, 4500); + { kinds: PROFILE_KINDS, authors: [pk], limit: 30, until: oldest.created_at - 1 }, 4500); const seen = new Set(c.notes.map((e) => e.id)); const older = (evs || []) .filter((e) => !seen.has(e.id) && seen.add(e.id)) .sort((a, b) => b.created_at - a.created_at); - if (older.length) { await notesReady(older); c.notes = [...c.notes, ...older]; } + if (older.length) { await notesReady(older); c.notes = newestArticles([...c.notes, ...older]); } else c.end = true; // the relays have nothing older } catch {} finally { c.loadingMore = false; @@ -8515,7 +8534,7 @@ export function messagesFeature(ctx) { }, '\u22ef')), foreign ? h('div', { class: 'note-text small muted', style: 'white-space:pre-wrap;overflow-wrap:anywhere' }, (ev.kind === 64 ? '\u265f ' : '') + altText) - : h('div', { class: 'note-text', style: 'white-space:pre-wrap;overflow-wrap:anywhere' }, ...(ev.kind === ARTICLE ? articleBody(ev) : noteBody(ev.content, 0, emojiTagMap(ev.tags)))), + : h('div', { class: 'note-text', style: 'white-space:pre-wrap;overflow-wrap:anywhere' }, ...(ev.kind === ARTICLE ? (open ? [articleCard(ev)] : articleBody(ev)) : noteBody(ev.content, 0, emojiTagMap(ev.tags)))), ev.kind === POLL ? pollBox(ev) : null, pending ? null : noteActions(pk, ev, { canZap }), !pending && whoOpen(ev.id) ? whoPanel(ev) : null)); |
