History

Fix snapshot sync bypassing recovery coordinator and dead loading state

- Remove refreshActiveSnapshot from unarchiveThread: the EventRouter's recovery coordinator already handles thread.unarchived events via fallbackToSnapshotRecovery, which properly updates latestSequence. Calling syncServerReadModel directly left the coordinator stale, causing already-applied events to be re-processed and potentially corrupting streaming message content via double concatenation. - Fix archived threads loading state: placeholderData always returned EMPTY_ARCHIVED_THREADS on first load, making isPending permanently false and the loading UI unreachable. Now only uses previous query data as placeholder, allowing isPending to be true on first fetch.

Cursor Agent committed 7 months ago

Commit 95dd2b61 parent bbe5e142 files · +2 −13Browse files

Unified · Split

2 files
  1. apps/web/src/hooks/useThreadActions.ts +1 −10
  2. apps/web/src/lib/orchestrationReactQuery.ts +1 −3
apps/web/src/hooks/useThreadActions.ts+1−10
@@ -18,7 +18,6 @@ import { useSettings } from "./useSettings"; · 17 unmodified lines
1818
1919 export function useThreadActions() {
2020 const appSettings = useSettings();
21− const syncServerReadModel = useStore((store) => store.syncServerReadModel);
2221 const clearComposerDraftForThread = useComposerDraftStore((store) => store.clearDraftThread);
2322 const clearProjectDraftThreadById = useComposerDraftStore(
2423 (store) => store.clearProjectDraftThreadById,
@@ -33,13 +32,6 @@ export function useThreadActions() { · 8 unmodified lines
3332 const queryClient = useQueryClient();
3433 const removeWorktreeMutation = useMutation(gitRemoveWorktreeMutationOptions({ queryClient }));
3534
36− const refreshActiveSnapshot = useCallback(async () => {
37− const api = readNativeApi();
38− if (!api) return;
39− const snapshot = await api.orchestration.getActiveSnapshot();
40− syncServerReadModel(snapshot);
41− }, [syncServerReadModel]);
42−
4335 const archiveThread = useCallback(
4436 async (threadId: ThreadId) => {
4537 const api = readNativeApi();
@@ -73,10 +65,9 @@ export function useThreadActions() { · 27 unmodified lines
7365 commandId: newCommandId(),
7466 threadId,
7567 });
76− await refreshActiveSnapshot();
7768 void queryClient.invalidateQueries({ queryKey: orchestrationQueryKeys.archivedThreads() });
7869 },
79− [queryClient, refreshActiveSnapshot],
70+ [queryClient],
8071 );
8172
8273 const deleteThread = useCallback(
apps/web/src/lib/orchestrationReactQuery.ts+1−3
@@ -1,4 +1,3 @@
1−import type { OrchestrationListArchivedThreadsResult } from "@t3tools/contracts";
21 import { queryOptions } from "@tanstack/react-query";
32 import { ensureNativeApi } from "~/nativeApi";
43
@@ -8,7 +7,6 @@ export const orchestrationQueryKeys = { · 3 unmodified lines
87 };
98
109 const DEFAULT_ARCHIVED_THREADS_STALE_TIME = 15_000;
11−const EMPTY_ARCHIVED_THREADS: OrchestrationListArchivedThreadsResult = [];
1210
1311 export function archivedThreadsQueryOptions() {
1412 return queryOptions({
@@ -18,6 +16,6 @@ export function archivedThreadsQueryOptions() { · 3 unmodified lines
1816 return api.orchestration.listArchivedThreads();
1917 },
2018 staleTime: DEFAULT_ARCHIVED_THREADS_STALE_TIME,
21− placeholderData: (previous) => previous ?? EMPTY_ARCHIVED_THREADS,
19+ placeholderData: (previous) => previous,
2220 });
2321 }