diff options
| author | Adam Malczewski <[email protected]> | 2026-06-22 00:36:31 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-22 00:36:31 +0900 |
| commit | 54e88b71efd9a6fd9d880b6e90d844a875808662 (patch) | |
| tree | 7d8292486f845225f4f03801531db2dc6ba8b7b1 /src/core | |
| parent | a8de5b2b9bec07a5ed5df54b859fa6ff5f98406f (diff) | |
| download | dispatch-web-54e88b71efd9a6fd9d880b6e90d844a875808662.tar.gz dispatch-web-54e88b71efd9a6fd9d880b6e90d844a875808662.zip | |
feat(tabs): cross-device tab sync via conversation lifecycle
Consume the conversation lifecycle handoff ([email protected], [email protected]).
Re-pinned file: deps + re-mirrored .dispatch/*.reference.md.
- fetchOpenConversations() on connect: GET /conversations?status=active,idle
restores the tab bar across devices (merges with localStorage — opens new
tabs, removes closed ones, updates titles from backend)
- conversation.statusChanged WS handler: closed → removeTabLocally (no
re-POST); active → open tab + spinner; idle → update status map
- conversation.compacted WS handler: dispose stale store + cache, reload
history from server
- TabBar shows a spinner on active conversations (statusFor prop)
- closeTab refactored to use removeTabLocally (extracted cleanup)
- conformance guards + WS adapter tests cover all 3 new WsServerMessage types
686 tests green.
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/wire/conformance.test.ts | 13 | ||||
| -rw-r--r-- | src/core/wire/conformance.ts | 4 |
2 files changed, 17 insertions, 0 deletions
diff --git a/src/core/wire/conformance.test.ts b/src/core/wire/conformance.test.ts index f5d6608..58cba3a 100644 --- a/src/core/wire/conformance.test.ts +++ b/src/core/wire/conformance.test.ts @@ -140,6 +140,17 @@ describe("classifies every WsServerMessage type", () => { }, { type: "chat.error" as const, message: "e" }, { type: "conversation.open" as const, conversationId: "c1" }, + { + type: "conversation.statusChanged" as const, + conversationId: "c1", + status: "active" as const, + }, + { + type: "conversation.compacted" as const, + conversationId: "c1", + messagesSummarized: 10, + messagesKept: 5, + }, ]; const labels = msgs.map(assertWsServerMessageExhaustive); expect(labels).toEqual([ @@ -150,6 +161,8 @@ describe("classifies every WsServerMessage type", () => { "chat.delta", "chat.error", "conversation.open", + "conversation.statusChanged", + "conversation.compacted", ]); }); }); diff --git a/src/core/wire/conformance.ts b/src/core/wire/conformance.ts index 05a15aa..07808fc 100644 --- a/src/core/wire/conformance.ts +++ b/src/core/wire/conformance.ts @@ -83,6 +83,10 @@ export function assertWsServerMessageExhaustive(msg: WsServerMessage): string { return "chat.error"; case "conversation.open": return "conversation.open"; + case "conversation.statusChanged": + return "conversation.statusChanged"; + case "conversation.compacted": + return "conversation.compacted"; default: return msg satisfies never; } |
