// Renders a single normalized activity entry into an AIM IM-window line. // `ctx` carries the resolve maps: ctx.posts (uri -> post view) and // ctx.profiles (did -> profile view), filled by chat.js before rendering. import { esc, clip, shortDid, fmtTime, postUrl } from './util.js'; const VERB = { block: 'blocked someone', profile: 'updated their profile', other: 'did something', }; // The quote block. When we can build a bsky.app URL for the post, it becomes a // clickable link to that post; otherwise a plain div. function quote(txt, who, uri) { const w = who ? `@${esc(who)} ` : ''; const inner = `${w}${txt ? esc(clip(txt)) : '(no text)'}`; const url = postUrl(uri); return url ? `${inner}` : `
${inner}
`; } function bodyHtml(e, ctx) { switch (e.kind) { case 'post': case 'reply': return `${e.kind === 'reply' ? 'replied' : 'posted'}` + quote(e.text + (e.img ? ' 🖼' : ''), null, e.uri); case 'repost': { const tgt = e.targetUri ? ctx.posts[e.targetUri] : null; return `🔁 reposted` + quote(e.text || tgt?.record?.text || '', e.targetHandle || tgt?.author?.handle, e.uri); } case 'like': { const tgt = e.targetUri ? ctx.posts[e.targetUri] : null; return `❤ liked` + quote(tgt?.record?.text || '', tgt?.author?.handle, e.uri); } case 'follow': { const p = e.targetDid ? ctx.profiles[e.targetDid] : null; const h = p?.handle || e.targetHandle || (e.targetDid ? shortDid(e.targetDid) : 'someone'); return `➕ followed @${esc(h)}`; } default: return `${VERB[e.kind] || 'did something'}`; } } export function lineHtml(buddy, e, ctx) { const isMe = e.dir === 'me'; const name = isMe ? 'you' : (buddy.profile?.displayName?.trim() || buddy.profile?.handle || 'buddy'); const t = e.timeMs ? fmtTime(e.timeMs) : ''; return `
${esc(name)} ${t}
${bodyHtml(e, ctx)}
`; }