import { useState, useEffect, createContext, useContext } from 'react'; import { Routes, Route, Navigate, useNavigate } from 'react-router-dom'; import { User, setOnTokenRefreshed } from './lib/api'; import { socketClient } from './lib/socket'; import Header from './components/Header'; import StatusBar from './components/StatusBar'; import Home from './pages/Home'; import ProjectManager from './pages/ProjectManager'; import SignIn from './pages/SignIn'; import ChatSessionView from './pages/ChatSessionView'; import DroneManager from './pages/DroneManager'; import Settings from './pages/Settings'; const TOKEN_KEY = 'dtp_auth_token'; const USER_KEY = 'dtp_user'; const PROJECT_KEY = 'dtp_current_project'; function getStoredUser(): User | null { try { const userData = localStorage.getItem(USER_KEY); return userData ? JSON.parse(userData) : null; } catch { return null; } } function setStoredUser(user: User | null) { if (user) { localStorage.setItem(USER_KEY, JSON.stringify(user)); } else { localStorage.removeItem(USER_KEY); } } export function getStoredProject(): string | null { return localStorage.getItem(PROJECT_KEY); } export function setStoredProject(slug: string | null) { if (slug) { localStorage.setItem(PROJECT_KEY, slug); } else { localStorage.removeItem(PROJECT_KEY); } } interface AppContextType { user: User | null; updateUser: (user: User) => void; currentProject: string | null; setCurrentProject: (slug: string | null) => void; onSignOut: () => void; statusMessage: string; setStatusMessage: (message: string) => void; } export const AppContext = createContext(null); export function useAppContext(): AppContextType { const ctx = useContext(AppContext); if (!ctx) throw new Error('useAppContext must be used within App provider'); return ctx; } export default function App() { const navigate = useNavigate(); const [user, setUser] = useState(null); const [currentProject, setCurrentProject] = useState(null); const [loading, setLoading] = useState(true); const [statusMessage, setStatusMessage] = useState('Ready.'); useEffect(() => { const storedUser = getStoredUser(); const storedToken = localStorage.getItem(TOKEN_KEY); const storedProject = localStorage.getItem(PROJECT_KEY); if (storedUser && storedToken) { setUser(storedUser); socketClient.connect(storedToken); } if (storedProject) { setCurrentProject(storedProject); } // Register callback so the socket client updates its JWT whenever // the API client refreshes the token. This ensures that future // socket reconnections use the new token instead of the expired one. setOnTokenRefreshed((newToken: string) => { socketClient.updateJwt(newToken); }); setLoading(false); }, []); const handleSignInSuccess = (newUser: User, token: string) => { localStorage.setItem(TOKEN_KEY, token); setStoredUser(newUser); setUser(newUser); socketClient.connect(token); }; const handleUpdateUser = (updatedUser: User) => { setStoredUser(updatedUser); setUser(updatedUser); }; const handleSignOut = async () => { try { await fetch('/api/v1/auth/sign-out', { method: 'GET', credentials: 'include', }); } catch { // Ignore errors — we're signing out regardless } localStorage.removeItem(TOKEN_KEY); localStorage.removeItem(PROJECT_KEY); setStoredUser(null); setUser(null); setCurrentProject(null); socketClient.disconnect(); navigate('/'); }; const handleSetCurrentProject = (slug: string | null) => { setStoredProject(slug); setCurrentProject(slug); }; if (loading) { return (
Loading...
); } return (
} /> } /> } /> } /> } /> : } /> } /> ) : ( ) } />
); }