// 1. Initial render.
const { conversations, nextCursor } = await fetch12m(
`/v1/identities/${handle}/conversations?limit=50`,
);
const list = new Map(conversations.map((c) => [c.id, c]));
let scrollCursor = nextCursor;
// 2. Subscribe to deltas.
let eventCursor = 0;
while (mounted) {
const r = await fetch12m(
`/v1/identities/${handle}/events?since=${eventCursor}&timeoutMs=25000`,
);
for (const ev of r.events) {
if (!ev.convId) continue;
if (list.has(ev.convId)) {
// Existing row — patch from the event.
const row = list.get(ev.convId);
row.lastEventAt = ev.ts;
if (ev.data?.message?.snippet) row.snippet = ev.data.message.snippet;
if (ev.data?.message?.subject) row.subject = ev.data.message.subject;
if (ev.type === "email.received" || ev.type === "email.replied") {
row.messageCount = (row.messageCount ?? 0) + 1;
}
} else {
// New thread — fetch the row shape, prepend.
const { conversation } = await fetch12m(
`/v1/identities/${handle}/conversations/${ev.convId}`,
);
list.set(conversation.id, conversation);
}
render(Array.from(list.values()).sort((a, b) => b.lastEventAt - a.lastEventAt));
}
eventCursor = r.cursor;
}
// 3. Pagination — when the user scrolls past the bottom.
async function loadOlder() {
if (!scrollCursor) return;
const older = await fetch12m(
`/v1/identities/${handle}/conversations?cursor=${scrollCursor}&limit=50`,
);
for (const c of older.conversations) list.set(c.id, c);
scrollCursor = older.nextCursor;
}