Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109// Normalizes the two activity sources (the public author feed, and buffered// jetstream commit events) into a single "entry" shape the chat renderer// understands. The stable `key` lets us de-dupe across both sources.//// entry: { key, timeMs, kind, text?, uri?, targetUri?, targetDid?, targetHandle?,// img?, targets[], dir? }// uri = the post this entry's body refers to (own / liked / reposted) —// makes the quote block a clickable link.// targets = DIDs this action is directed AT (reply-parent, quoted/mentioned// author, liked/reposted author, followed DID). Used to scope a chat// window to interactions between two specific people.// dir = 'them' | 'me', assigned by the caller (whose feed it came from).// kind: post | reply | repost | like | follow | block | profile | other
const AT = 'at://';
function embedHasImage(embed) { const t = embed?.$type || ''; return t.includes('images') || t.includes('video') || t.includes('external');}
// did out of an at:// uri, or null.const uriDid = (uri) => uri?.match(/^at:\/\/(did:[a-z0-9:]+)\//i)?.[1] || null;
// Who a post/reply record is aimed at: the parent author, a quoted author, and// any @-mentions.function recordTargets(rec) { const out = []; const parent = uriDid(rec?.reply?.parent?.uri); if (parent) out.push(parent); const emb = rec?.embed; const quoted = emb?.$type === 'app.bsky.embed.record' ? emb.record?.uri : emb?.$type === 'app.bsky.embed.recordWithMedia' ? emb.record?.record?.uri : null; const qd = uriDid(quoted); if (qd) out.push(qd); for (const f of rec?.facets || []) { for (const ft of f.features || []) { if (ft?.$type === 'app.bsky.richtext.facet#mention' && ft.did) out.push(ft.did); } } return out;}
// From a getAuthorFeed item (has real post/repost TEXT, but never likes/follows).export function entryFromFeedItem(item) { const post = item.post; if (!post) return null; const rec = post.record || {}; const img = embedHasImage(rec.embed); const isRepost = item.reason?.$type === 'app.bsky.feed.defs#reasonRepost'; if (isRepost) { return { key: `repost:${post.uri}`, uri: post.uri, timeMs: Date.parse(item.reason.indexedAt || post.indexedAt) || 0, kind: 'repost', targetHandle: post.author?.handle, targets: [post.author?.did].filter(Boolean), text: rec.text || '', img, }; } return { key: `post:${post.uri}`, uri: post.uri, timeMs: Date.parse(rec.createdAt || post.indexedAt) || 0, kind: item.reply ? 'reply' : 'post', targets: recordTargets(rec), text: rec.text || '', img, };}
// From a buffered jetstream commit (carries likes/follows the feed can't, but// only the subject URI/DID — the renderer resolves those to text via getPosts).export function entryFromBuffer(ev) { const r = ev.record || {}; const base = { timeMs: ev.timeMs, img: false }; switch (ev.collection) { case 'app.bsky.feed.post': { const uri = `${AT}${ev.did}/app.bsky.feed.post/${ev.rkey}`; return { ...base, key: `post:${uri}`, uri, kind: r.reply ? 'reply' : 'post', text: r.text || '', img: embedHasImage(r.embed), targets: recordTargets(r) }; } case 'app.bsky.feed.repost': return { ...base, key: `repost:${r.subject?.uri}`, uri: r.subject?.uri, kind: 'repost', targetUri: r.subject?.uri, targets: [uriDid(r.subject?.uri)].filter(Boolean) }; case 'app.bsky.feed.like': return { ...base, key: `like:${r.subject?.uri}`, uri: r.subject?.uri, kind: 'like', targetUri: r.subject?.uri, targets: [uriDid(r.subject?.uri)].filter(Boolean) }; case 'app.bsky.graph.follow': return { ...base, key: `follow:${r.subject}`, kind: 'follow', targetDid: r.subject, targets: [r.subject].filter(Boolean) }; case 'app.bsky.graph.block': return { ...base, key: `block:${ev.rkey}`, kind: 'block', targets: [] }; case 'app.bsky.actor.profile': return { ...base, key: `profile:${ev.timeMs}`, kind: 'profile', targets: [] }; default: return { ...base, key: `${ev.collection}:${ev.rkey}`, kind: 'other', text: ev.collection, targets: [] }; }}
// Does this action target a given person?export const involves = (entry, did) => !!did && entry.targets?.includes(did);
// Which entries still need a network resolve before they can render fully.export const needsPostResolve = (e) => (e.kind === 'like' || e.kind === 'repost') && e.targetUri && !e.text;export const needsProfileResolve = (e) => e.kind === 'follow' && !!e.targetDid;