Files
multica/packages/core/chat/unread.test.ts
Jiayuan Zhang e10bb3fff0 fix(chat): unify unread badge counting across sidebar, mobile, and thread list (MUL-4286) (#5239)
The aggregate chat unread badge used two different definitions: web/
desktop's sidebar summed unread messages while mobile's tab badge (and
the since-removed ChatFab badge it mirrored) counted sessions — the same
account state showed e.g. 5 on web and 2 on iOS. The sidebar also summed
ALL sessions while the thread list zeroes the row being viewed, so a
reply landing in the open conversation flashed a sidebar count with no
matching row.

- packages/core/chat/unread.ts: countUnreadChatMessages() as the single
  shared definition (IM-style message total, optional viewed-session
  exclusion); pure so mobile can import it.
- app-sidebar: use the helper and exclude the actively-viewed session,
  but only while a chat surface is actually showing it (chat route or
  floating window open) — a remembered selection with both surfaces
  closed still counts, since nothing will auto mark-read there.
- mobile: tab badge switches to the shared message count (99+ cap like
  the sidebar), ChatSessionSchema parses unread_count, and markRead's
  optimistic patch zeroes unread_count alongside has_unread.

Co-authored-by: Lambda <lambda@multica.ai>
Co-authored-by: multica-agent <github@multica.ai>
2026-07-11 02:46:12 +08:00

47 lines
1.5 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { countUnreadChatMessages } from "./unread";
import type { ChatSession } from "../types/chat";
function session(id: string, unread_count?: number): ChatSession {
return {
id,
workspace_id: "ws-1",
agent_id: "agent-1",
creator_id: "user-1",
title: id,
status: "active",
has_unread: (unread_count ?? 0) > 0,
unread_count,
created_at: "2026-07-10T00:00:00Z",
updated_at: "2026-07-10T00:00:00Z",
};
}
describe("countUnreadChatMessages", () => {
it("sums unread messages across sessions (not session count)", () => {
expect(
countUnreadChatMessages([session("a", 2), session("b", 3), session("c", 0)]),
).toBe(5);
});
it("treats a missing unread_count (older server) as 0", () => {
expect(countUnreadChatMessages([session("a"), session("b", 4)])).toBe(4);
});
it("returns 0 for an empty or unloaded list", () => {
expect(countUnreadChatMessages([])).toBe(0);
expect(countUnreadChatMessages(undefined)).toBe(0);
});
it("excludes the session being viewed from the sum", () => {
const sessions = [session("a", 2), session("b", 3)];
expect(countUnreadChatMessages(sessions, "a")).toBe(3);
expect(countUnreadChatMessages(sessions, "b")).toBe(2);
});
it("ignores an exclude id that is not in the list", () => {
expect(countUnreadChatMessages([session("a", 2)], "ghost")).toBe(2);
expect(countUnreadChatMessages([session("a", 2)], null)).toBe(2);
});
});