blob: c6c9b90caa6780848f9c0ed6c419f3be0c4e39ef [file]
import type { SessionSummary, StoredMessage } from '@maka/core';
export type SessionReadBoundaries = Record<string, number>;
export interface SessionListRefresher {
refresh(): Promise<SessionSummary[]>;
}
export interface SessionListRefresherOptions {
listSessions: () => Promise<SessionSummary[]>;
readBoundaries: () => Readonly<SessionReadBoundaries>;
currentSessions: () => SessionSummary[];
commitSessions: (sessions: SessionSummary[]) => void;
onError: (error: unknown) => void;
}
export function rememberSessionReadBoundary(
boundaries: SessionReadBoundaries,
sessionId: string,
messages: readonly StoredMessage[],
): void {
const boundary = latestMessageTs(messages);
if (boundary === undefined) return;
boundaries[sessionId] = Math.max(boundaries[sessionId] ?? 0, boundary);
}
export function applySessionReadOverrides(
sessions: SessionSummary[],
boundaries: Readonly<SessionReadBoundaries>,
): SessionSummary[] {
let changed = false;
const next = sessions.map((session) => {
const boundary = boundaries[session.id];
if (boundary === undefined || !session.hasUnread) return session;
if ((session.lastMessageAt ?? 0) > boundary) return session;
changed = true;
return { ...session, hasUnread: false };
});
return changed ? next : sessions;
}
export function applyLocalSessionRead(
boundaries: SessionReadBoundaries,
sessions: SessionSummary[],
sessionId: string,
readMessages: readonly StoredMessage[],
): SessionSummary[] {
rememberSessionReadBoundary(boundaries, sessionId, readMessages);
return applySessionReadOverrides(sessions, boundaries);
}
export function createSessionListRefresher(options: SessionListRefresherOptions): SessionListRefresher {
let latestRequestId = 0;
return {
async refresh(): Promise<SessionSummary[]> {
const requestId = ++latestRequestId;
try {
const listed = await options.listSessions();
if (requestId !== latestRequestId) return options.currentSessions();
const next = applySessionReadOverrides(listed, options.readBoundaries());
options.commitSessions(next);
return next;
} catch (error) {
if (requestId === latestRequestId) options.onError(error);
return options.currentSessions();
}
},
};
}
function latestMessageTs(messages: readonly StoredMessage[]): number | undefined {
let latest: number | undefined;
for (const message of messages) {
if (!Number.isFinite(message.ts)) continue;
latest = latest === undefined ? message.ts : Math.max(latest, message.ts);
}
return latest;
}