import type { ChatMessage, SessionSnapshot } from "../../../shared/types"; import type { ChatThread } from "./types"; import { tStatic } from "../i18n"; function getLastMessage(messages: ChatMessage[]): ChatMessage | null { if (messages.length === 0) return null; return messages.reduce((latest, current) => current.createdAt > latest.createdAt ? current : latest, ); } export function buildThreads( session: SessionSnapshot, meId: string | null, isBanker: boolean, ): ChatThread[] { const threads: ChatThread[] = []; const globalMessages = session.chats.filter((message) => message.groupId === null); threads.push({ id: "global", name: tStatic("chat.global"), kind: "global", members: [], lastMessage: getLastMessage(globalMessages), }); session.groups.forEach((group) => { if (!isBanker && meId && !group.memberIds.includes(meId)) { return; } const groupMessages = session.chats.filter((message) => message.groupId === group.id); let name = group.name; let kind: ChatThread["kind"] = "group"; if (group.memberIds.length === 2) { const [first, second] = group.memberIds; if (isBanker || !meId) { const firstName = session.players.find((player) => player.id === first)?.name ?? tStatic("common.player"); const secondName = session.players.find((player) => player.id === second)?.name ?? tStatic("common.player"); name = `${firstName} & ${secondName}`; kind = "direct"; } else { const otherId = first === meId ? second : first; const otherName = session.players.find((player) => player.id === otherId)?.name ?? tStatic("common.player"); name = otherName; kind = "direct"; } } threads.push({ id: group.id, name, kind, members: group.memberIds, lastMessage: getLastMessage(groupMessages), }); }); return threads.sort((a, b) => { const aTime = a.lastMessage?.createdAt ?? 0; const bTime = b.lastMessage?.createdAt ?? 0; return bTime - aTime; }); } export function getThreadMessages(session: SessionSnapshot, threadId: string): ChatMessage[] { if (threadId === "global") { return session.chats.filter((message) => message.groupId === null).slice().reverse(); } return session.chats.filter((message) => message.groupId === threadId).slice().reverse(); } export function getLatestThreadTimestamp(session: SessionSnapshot, threadId: string): number | null { const messages = threadId === "global" ? session.chats.filter((message) => message.groupId === null) : session.chats.filter((message) => message.groupId === threadId); if (messages.length === 0) return null; return messages.reduce( (latest, message) => (message.createdAt > latest ? message.createdAt : latest), 0, ); } export function getUnreadThreadIds( session: SessionSnapshot, meId: string | null, isBanker: boolean, readState: Record, ): Set { const unread = new Set(); if (!meId) return unread; const allowed = new Set( buildThreads(session, meId, isBanker).map((thread) => thread.id), ); session.chats.forEach((message) => { if (message.fromId === meId) return; const threadId = message.groupId ?? "global"; if (!allowed.has(threadId)) return; const lastRead = readState[threadId] ?? 0; if (message.createdAt > lastRead) { unread.add(threadId); } }); return unread; }