diff --git a/src/components/auth/CryptIDDropdown.tsx b/src/components/auth/CryptIDDropdown.tsx index baa6809..7291279 100644 --- a/src/components/auth/CryptIDDropdown.tsx +++ b/src/components/auth/CryptIDDropdown.tsx @@ -110,16 +110,22 @@ const CryptIDDropdown: React.FC = ({ isDarkMode = false }) } }, [session.authed, session.username]); - // Load connections when authenticated + // Load connections when authenticated, clear when logged out useEffect(() => { const loadConnections = async () => { - if (!session.authed || !session.username) return; + if (!session.authed || !session.username) { + // Clear connections state when user logs out + setConnections([]); + setConnectionsLoading(false); + return; + } setConnectionsLoading(true); try { const myConnections = await getMyConnections(); setConnections(myConnections as UserConnectionWithProfile[]); } catch (error) { console.error('Failed to load connections:', error); + setConnections([]); // Clear on error too } finally { setConnectionsLoading(false); } @@ -127,6 +133,23 @@ const CryptIDDropdown: React.FC = ({ isDarkMode = false }) loadConnections(); }, [session.authed, session.username]); + // Listen for session-cleared event to immediately clear connections state + useEffect(() => { + const handleSessionCleared = () => { + console.log('🔐 CryptIDDropdown: Session cleared - resetting connections state'); + setConnections([]); + setConnectionsLoading(false); + setShowDropdown(false); + setShowCryptIDModal(false); + setExpandedSection('none'); + setEditingConnectionId(null); + setEditingMetadata({}); + }; + + window.addEventListener('session-cleared', handleSessionCleared); + return () => window.removeEventListener('session-cleared', handleSessionCleared); + }, []); + // Connection handlers const handleConnect = async (userId: string, trustLevel: TrustLevel) => { if (!session.authed || !session.username) return; diff --git a/src/components/networking/useNetworkGraph.ts b/src/components/networking/useNetworkGraph.ts index 7cbc085..af3c99e 100644 --- a/src/components/networking/useNetworkGraph.ts +++ b/src/components/networking/useNetworkGraph.ts @@ -310,6 +310,24 @@ export function useNetworkGraph(options: UseNetworkGraphOptions = {}): UseNetwor fetchGraph(); }, [fetchGraph]); + // Listen for session-cleared event to immediately clear graph state + useEffect(() => { + const handleSessionCleared = () => { + console.log('🔐 useNetworkGraph: Session cleared - resetting graph state'); + clearGraphCache(); + setState({ + nodes: [], + edges: [], + myConnections: [], + isLoading: false, + error: null, + }); + }; + + window.addEventListener('session-cleared', handleSessionCleared); + return () => window.removeEventListener('session-cleared', handleSessionCleared); + }, []); + // Refresh interval useEffect(() => { if (refreshInterval > 0) { diff --git a/src/lib/auth/sessionPersistence.ts b/src/lib/auth/sessionPersistence.ts index bb10f4c..bd9b2d5 100644 --- a/src/lib/auth/sessionPersistence.ts +++ b/src/lib/auth/sessionPersistence.ts @@ -18,7 +18,7 @@ export interface StoredSession { */ export const saveSession = (session: Session): boolean => { if (typeof window === 'undefined') return false; - + try { const storedSession: StoredSession = { username: session.username, @@ -28,8 +28,18 @@ export const saveSession = (session: Session): boolean => { obsidianVaultPath: session.obsidianVaultPath, obsidianVaultName: session.obsidianVaultName }; - + localStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(storedSession)); + + // Dispatch event to notify components that session was saved (e.g., after login) + // This helps components like Board.tsx update their state immediately + if (session.authed && session.username) { + window.dispatchEvent(new CustomEvent('session-logged-in', { + detail: { username: session.username } + })); + console.log('🔐 Session saved and session-logged-in event dispatched for:', session.username); + } + return true; } catch (error) { console.error('🔧 Error saving session:', error); @@ -72,15 +82,57 @@ export const loadSession = (): StoredSession | null => { }; /** - * Clear stored session + * Clear stored session and all related user data + * This ensures a clean slate when logging out */ export const clearStoredSession = (): boolean => { if (typeof window === 'undefined') return false; - + try { + // Get the current username before clearing (to clean up user-specific keys) + const currentSession = loadSession(); + const username = currentSession?.username; + + // Clear the main session localStorage.removeItem(SESSION_STORAGE_KEY); + + // IMPORTANT: Do NOT clear tldraw-user-id-* keys! + // These must persist so the same user keeps the same presence ID across login/logout cycles. + // If we clear them, each login creates a NEW presence ID, but old presence records + // persist in the shared Automerge document and sync back - causing stacked cursors. + + // IMPORTANT: Do NOT clear crypto keys or user account data - they must persist for re-login! + // The following are PRESERVED across logout (tied to user ACCOUNT, not session): + // - tldraw-user-id-${username} (presence ID - must stay same to avoid duplicate cursors) + // - ${username}_authData (crypto challenge/signature required for login) + // - ${username}_publicKey (public key for verification) + // - ${username}_fathomApiKey (integration credentials) + // - ${username}_miroApiKey (integration credentials) + // - registeredUsers (list of registered accounts on this device) + // + // Only SESSION-SPECIFIC data is cleared below: + + // Clear any cached permission data (session-specific) + localStorage.removeItem('boardPermissions'); + localStorage.removeItem('currentBoardPermission'); + + // Clear network graph cache to force fresh state (session-specific) + localStorage.removeItem('network_graph_cache'); + + // Clear current room ID to prevent stale room associations (session-specific) + localStorage.removeItem('currentRoomId'); + + // Dispatch event to notify all components to clear their state + // This helps ensure components like CryptIDDropdown, NetworkGraphPanel, etc. + // properly reset their internal state + window.dispatchEvent(new CustomEvent('session-cleared', { + detail: { previousUsername: username } + })); + + console.log('🔐 Session cleared - removed session state, preserved account data (crypto keys, tldraw IDs)'); return true; } catch (error) { + console.error('🔧 Error clearing session:', error); return false; } };