mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-05 16:13:08 +08:00
fix: wait for zustand hydration before workspace initialization
Fix blank page on first load via `ccw view` by waiting for zustand persist hydration to complete before initializing workspace. - Add _hasHydrated state tracking in workflowStore - Add setHasHydrated action to mark hydration complete - Update AppShell to wait for hydration before calling switchWorkspace - Ensures projectPath is properly restored from localStorage before queries execute
This commit is contained in:
@@ -40,15 +40,23 @@ export function AppShell({
|
||||
// Workspace initialization from URL query parameter
|
||||
const switchWorkspace = useWorkflowStore((state) => state.switchWorkspace);
|
||||
const projectPath = useWorkflowStore((state) => state.projectPath);
|
||||
const hasHydrated = useWorkflowStore((state) => state._hasHydrated);
|
||||
const location = useLocation();
|
||||
|
||||
// Immersive mode (fullscreen) - hide chrome
|
||||
const isImmersiveMode = useAppStore(selectIsImmersiveMode);
|
||||
|
||||
// Workspace initialization logic (URL > localStorage)
|
||||
// Wait for zustand persist hydration to complete before initializing
|
||||
const [isWorkspaceInitialized, setWorkspaceInitialized] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Wait for hydration to complete before initializing workspace
|
||||
// This ensures projectPath is properly restored from localStorage
|
||||
if (!hasHydrated) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This effect should only run once to decide the initial workspace.
|
||||
if (isWorkspaceInitialized) {
|
||||
return;
|
||||
@@ -76,7 +84,7 @@ export function AppShell({
|
||||
|
||||
// Mark as initialized regardless of whether a path was found.
|
||||
setWorkspaceInitialized(true);
|
||||
}, [isWorkspaceInitialized, projectPath, location.search, switchWorkspace]);
|
||||
}, [hasHydrated, isWorkspaceInitialized, projectPath, location.search, switchWorkspace]);
|
||||
|
||||
// Sidebar collapse state – default to collapsed (hidden)
|
||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
|
||||
|
||||
@@ -56,6 +56,9 @@ const initialState: WorkflowState = {
|
||||
// Filters and sorting
|
||||
filters: defaultFilters,
|
||||
sorting: defaultSorting,
|
||||
|
||||
// Hydration state (internal)
|
||||
_hasHydrated: false,
|
||||
};
|
||||
|
||||
export const useWorkflowStore = create<WorkflowStore>()(
|
||||
@@ -414,6 +417,10 @@ export const useWorkflowStore = create<WorkflowStore>()(
|
||||
set({ _invalidateQueriesCallback: callback }, false, 'registerQueryInvalidator');
|
||||
},
|
||||
|
||||
setHasHydrated: (state: boolean) => {
|
||||
set({ _hasHydrated: state }, false, 'setHasHydrated');
|
||||
},
|
||||
|
||||
// ========== Filters and Sorting ==========
|
||||
|
||||
setFilters: (filters: Partial<WorkflowFilters>) => {
|
||||
@@ -538,6 +545,8 @@ export const useWorkflowStore = create<WorkflowStore>()(
|
||||
console.error('[WorkflowStore] Rehydration error:', error);
|
||||
return;
|
||||
}
|
||||
// Mark hydration as complete
|
||||
useWorkflowStore.getState().setHasHydrated(true);
|
||||
if (state?.projectPath) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
@@ -335,6 +335,9 @@ export interface WorkflowState {
|
||||
|
||||
// Query invalidation callback (internal)
|
||||
_invalidateQueriesCallback?: () => void;
|
||||
|
||||
// Hydration state (internal)
|
||||
_hasHydrated: boolean;
|
||||
}
|
||||
|
||||
export interface WorkflowActions {
|
||||
@@ -369,6 +372,7 @@ export interface WorkflowActions {
|
||||
removeRecentPath: (path: string) => Promise<void>;
|
||||
refreshRecentPaths: () => Promise<void>;
|
||||
registerQueryInvalidator: (callback: () => void) => void;
|
||||
setHasHydrated: (state: boolean) => void;
|
||||
|
||||
// Filters and sorting
|
||||
setFilters: (filters: Partial<WorkflowFilters>) => void;
|
||||
|
||||
Reference in New Issue
Block a user