summaryrefslogtreecommitdiffhomepage
path: root/src/features
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/features
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/features')
-rw-r--r--src/features/tabs/ui/TabBar.svelte6
1 files changed, 6 insertions, 0 deletions
diff --git a/src/features/tabs/ui/TabBar.svelte b/src/features/tabs/ui/TabBar.svelte
index 812a663..9d224b9 100644
--- a/src/features/tabs/ui/TabBar.svelte
+++ b/src/features/tabs/ui/TabBar.svelte
@@ -5,12 +5,15 @@
let {
tabs,
activeConversationId,
+ statusFor,
onSelect,
onClose,
onNewDraft,
}: {
tabs: readonly Tab[];
activeConversationId: string | null;
+ /** Returns the conversation's lifecycle status, or undefined when unknown. */
+ statusFor?: (conversationId: string) => string | undefined;
onSelect: (conversationId: string) => void;
onClose: (conversationId: string) => void;
onNewDraft: () => void;
@@ -84,6 +87,9 @@
{handles.get(tab.conversationId) ?? tab.conversationId}
</span>
<span class="min-w-0 flex-1 truncate text-left">{tab.title}</span>
+ {#if statusFor?.(tab.conversationId) === "active"}
+ <span class="loading loading-spinner loading-xs shrink-0 text-primary"></span>
+ {/if}
<button
class="btn btn-ghost btn-xs shrink-0"
aria-label="Close tab"