Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

53 linesCodeBlameRaw
1/**
2 * When messages shown outside their conversation count as read: g1t's
3 * answer on Home's Summarize card, or any other
4 * page that shows a conversation's messages. Pure rules, and the record of
5 * which conversations this page shows right now, so a toast, a sound or a
6 * system notification does not repeat what is on screen (lib/notify-client,
7 * lib/sound-events). The hook that uses them is lib/use-read-when-shown.ts.
8 */
9
10/** What a surface knows when it might mark its conversation read. */
11export type ReadMoment = {
12 /** `document.visibilityState === "visible"`. */
13 visible: boolean;
14 /** The messages are on screen: the panel open, the card scrolled into view. */
15 onScreen: boolean;
16 /** The newest message shown and finished, or null while there is none. */
17 newestId: string | null;
18 /** The newest this surface has already marked read. */
19 lastRead: string | null;
20};
21
22/**
23 * The message to mark read now, or null: only while the page is visible
24 * and the messages are on screen, each newest id once, and only forward
25 * (ids sort by time).
26 */
27export function readNow(moment: ReadMoment): string | null {
28 if (!moment.visible || !moment.onScreen || !moment.newestId) return null;
29 if (moment.lastRead !== null && moment.newestId <= moment.lastRead) return null;
30 return moment.newestId;
31}
32
33// ── Conversations shown on this page ─────────────────────────────────────
34
35const shown = new Map<string, number>();
36
37/** This page shows `channelId`'s messages until the returned function is called. Counted: two surfaces may show the same one. */
38export function showConversation(channelId: string): () => void {
39 shown.set(channelId, (shown.get(channelId) ?? 0) + 1);
40 let done = false;
41 return () => {
42 if (done) return;
43 done = true;
44 const left = (shown.get(channelId) ?? 1) - 1;
45 if (left > 0) shown.set(channelId, left);
46 else shown.delete(channelId);
47 };
48}
49
50/** The conversations this page shows right now, besides the one open in Chat. */
51export function shownConversations(): ReadonlySet<string> {
52 return new Set(shown.keys());
53}