mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-10 17:11:04 +08:00
fix(AppShell): handle store rehydration errors gracefully and improve initialization logic
This commit is contained in:
@@ -19,7 +19,6 @@ import { useNotificationStore, selectCurrentQuestion, selectCurrentPopupCard } f
|
|||||||
import { useWorkflowStore } from '@/stores/workflowStore';
|
import { useWorkflowStore } from '@/stores/workflowStore';
|
||||||
import { useAppStore, selectIsImmersiveMode } from '@/stores/appStore';
|
import { useAppStore, selectIsImmersiveMode } from '@/stores/appStore';
|
||||||
import { useWebSocketNotifications, useWebSocket } from '@/hooks';
|
import { useWebSocketNotifications, useWebSocket } from '@/hooks';
|
||||||
import { useHasHydrated } from '@/hooks/useHasHydrated';
|
|
||||||
|
|
||||||
export interface AppShellProps {
|
export interface AppShellProps {
|
||||||
/** Callback for refresh action */
|
/** Callback for refresh action */
|
||||||
@@ -41,25 +40,33 @@ export function AppShell({
|
|||||||
// Workspace initialization from URL query parameter
|
// Workspace initialization from URL query parameter
|
||||||
const switchWorkspace = useWorkflowStore((state) => state.switchWorkspace);
|
const switchWorkspace = useWorkflowStore((state) => state.switchWorkspace);
|
||||||
const projectPath = useWorkflowStore((state) => state.projectPath);
|
const projectPath = useWorkflowStore((state) => state.projectPath);
|
||||||
const hasHydrated = useHasHydrated();
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
// Manually trigger hydration on mount (needed because of skipHydration: true in store config)
|
// Manually trigger hydration on mount (needed because of skipHydration: true in store config)
|
||||||
|
// Note: rehydrate() may throw TDZ errors due to circular dependencies in bundled code.
|
||||||
|
// This is non-fatal because loadPersistedPath() already provides projectPath synchronously.
|
||||||
|
const [isStoreReady, setStoreReady] = useState(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
useWorkflowStore.persist.rehydrate();
|
try {
|
||||||
|
useWorkflowStore.persist.rehydrate();
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[AppShell] Store rehydration failed (non-fatal, using initial state):', error);
|
||||||
|
}
|
||||||
|
setStoreReady(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Immersive mode (fullscreen) - hide chrome
|
// Immersive mode (fullscreen) - hide chrome
|
||||||
const isImmersiveMode = useAppStore(selectIsImmersiveMode);
|
const isImmersiveMode = useAppStore(selectIsImmersiveMode);
|
||||||
|
|
||||||
// Workspace initialization logic (URL > localStorage)
|
// Workspace initialization logic (URL > localStorage)
|
||||||
// Wait for zustand persist hydration to complete before initializing
|
// Uses isStoreReady instead of hasHydrated to avoid blocking when rehydration fails.
|
||||||
|
// loadPersistedPath() already provides projectPath synchronously at module init,
|
||||||
|
// so we don't need to wait for Zustand persist rehydration to complete.
|
||||||
const [isWorkspaceInitialized, setWorkspaceInitialized] = useState(false);
|
const [isWorkspaceInitialized, setWorkspaceInitialized] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Wait for hydration to complete before initializing workspace
|
// Wait for rehydration attempt to complete (success or failure)
|
||||||
// This ensures projectPath is properly restored from localStorage
|
if (!isStoreReady) {
|
||||||
if (!hasHydrated) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +77,7 @@ export function AppShell({
|
|||||||
|
|
||||||
const searchParams = new URLSearchParams(location.search);
|
const searchParams = new URLSearchParams(location.search);
|
||||||
const urlPath = searchParams.get('path');
|
const urlPath = searchParams.get('path');
|
||||||
const persistedPath = projectPath; // Path from rehydrated store
|
const persistedPath = projectPath; // Path from loadPersistedPath() or rehydrated store
|
||||||
|
|
||||||
// Priority 1: URL parameter.
|
// Priority 1: URL parameter.
|
||||||
if (urlPath) {
|
if (urlPath) {
|
||||||
@@ -79,7 +86,7 @@ export function AppShell({
|
|||||||
console.error('[AppShell] Failed to initialize from URL:', error);
|
console.error('[AppShell] Failed to initialize from URL:', error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Priority 2: Rehydrated path from localStorage.
|
// Priority 2: Persisted path from store (loaded synchronously via loadPersistedPath).
|
||||||
else if (persistedPath) {
|
else if (persistedPath) {
|
||||||
console.log('[AppShell] Initializing workspace from persisted state:', persistedPath);
|
console.log('[AppShell] Initializing workspace from persisted state:', persistedPath);
|
||||||
// The path is already in the store, but we need to trigger the data fetch.
|
// The path is already in the store, but we need to trigger the data fetch.
|
||||||
@@ -90,7 +97,7 @@ export function AppShell({
|
|||||||
|
|
||||||
// Mark as initialized regardless of whether a path was found.
|
// Mark as initialized regardless of whether a path was found.
|
||||||
setWorkspaceInitialized(true);
|
setWorkspaceInitialized(true);
|
||||||
}, [hasHydrated, isWorkspaceInitialized, projectPath, location.search, switchWorkspace]);
|
}, [isStoreReady, isWorkspaceInitialized, projectPath, location.search, switchWorkspace]);
|
||||||
|
|
||||||
// Sidebar collapse state – default to collapsed (hidden)
|
// Sidebar collapse state – default to collapsed (hidden)
|
||||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
// Manages workflow sessions, tasks, and related data
|
// Manages workflow sessions, tasks, and related data
|
||||||
|
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
import type {
|
import type {
|
||||||
WorkflowStore,
|
WorkflowStore,
|
||||||
WorkflowState,
|
WorkflowState,
|
||||||
|
|||||||
Reference in New Issue
Block a user