summaryrefslogtreecommitdiffhomepage
path: root/src/core/wire
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-22 00:36:31 +0900
committerAdam Malczewski <[email protected]>2026-06-22 00:36:31 +0900
commit54e88b71efd9a6fd9d880b6e90d844a875808662 (patch)
tree7d8292486f845225f4f03801531db2dc6ba8b7b1 /src/core/wire
parenta8de5b2b9bec07a5ed5df54b859fa6ff5f98406f (diff)
downloaddispatch-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/wire')
-rw-r--r--src/core/wire/conformance.test.ts13
-rw-r--r--src/core/wire/conformance.ts4
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;
}