Track per-employee chat read state and surface real unread counts

Adds chat_read_state migration (employee_id + space_id, last read
message + timestamp) wired through ChatService.markSpaceRead, IPC
markChatSpaceRead, and an effect in ChatPage that flushes the active
space whenever the latest message id changes.

Shell.computeBadges now derives the Messages badge from
(message.createdAt > read_state.last_read_at) minus messages the viewer
sent, so the sidebar shows true unread instead of total throughput.
This commit is contained in:
ZacharyZhang-NY committed 2026-05-21 20:25:07 -04:00
1 parent b44e0e3657
commit 1f78ae6f2e
11 files changed
+100 -3

No files matched your search

+6
View File
@@ -89,6 +89,12 @@ export interface CreateChatSpaceInput {
visibility: "workspace" | "restricted";
}
export interface MarkChatSpaceReadInput {
orgId: string;
employeeId: string;
spaceId: string;
}
export interface SaveProviderCredentialInput {
orgId: string;
providerId: string;
@@ -8,6 +8,7 @@ import type {
CreateApprovalInput,
CreateChatSpaceInput,
CreateMigrationReportInput,
MarkChatSpaceReadInput,
CreateOrganizationInput,
CreateProviderResponseInput,
CreateScheduledPostInput,
@@ -40,6 +41,7 @@ export interface DesktopApi {
decideApproval(input: DecideApprovalInput): Promise<DesktopResult<AppSnapshot>>;
createChatSpace(input: CreateChatSpaceInput): Promise<DesktopResult<AppSnapshot>>;
sendMessage(input: SendMessageInput): Promise<DesktopResult<AppSnapshot>>;
markChatSpaceRead(input: MarkChatSpaceReadInput): Promise<DesktopResult<AppSnapshot>>;
saveProviderCredential(input: SaveProviderCredentialInput): Promise<DesktopResult<AppSnapshot>>;
createProviderResponse(input: CreateProviderResponseInput): Promise<DesktopResult<AppSnapshot>>;
importSkillDirectory(input: ImportSkillDirectoryInput): Promise<DesktopResult<AppSnapshot>>;
+9
View File
@@ -224,6 +224,14 @@ export interface ChatPresenceRecord {
lastSeenAt: string;
}
export interface ChatReadStateRecord {
orgId: string;
employeeId: string;
spaceId: string;
lastReadMessageId: string | null;
lastReadAt: string;
}
export interface FileObjectRecord {
id: string;
orgId: string;
@@ -407,6 +415,7 @@ export interface AppSnapshot {
chatThreads: ChatThreadRecord[];
chatAttachments: ChatAttachmentRecord[];
chatPresence: ChatPresenceRecord[];
chatReadStates: ChatReadStateRecord[];
files: FileObjectRecord[];
scheduledPosts: ScheduledPostRecord[];
providers: ProviderRecord[];
+1
View File
@@ -9,6 +9,7 @@ export const IPC_CHANNELS = {
decideApproval: "agentcompany:decide-approval",
createChatSpace: "agentcompany:create-chat-space",
sendMessage: "agentcompany:send-message",
markChatSpaceRead: "agentcompany:mark-chat-space-read",
saveProviderCredential: "agentcompany:save-provider-credential",
createProviderResponse: "agentcompany:create-provider-response",
importSkillDirectory: "agentcompany:import-skill-directory",