// 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)}
`;
}