150 lines
4.6 KiB
TypeScript
150 lines
4.6 KiB
TypeScript
import { useState, useEffect, createContext, useContext } from 'react';
|
|
import { BrowserRouter, Routes, Route, Navigate, useNavigate } from 'react-router-dom';
|
|
import { User } 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 SignUp from './pages/SignUp';
|
|
import ChatSessionView from './pages/ChatSessionView';
|
|
import DroneManager from './pages/DroneManager';
|
|
|
|
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;
|
|
currentProject: string | null;
|
|
setCurrentProject: (slug: string | null) => void;
|
|
onSignOut: () => void;
|
|
}
|
|
|
|
const AppContext = createContext<AppContextType | null>(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 [user, setUser] = useState<User | null>(null);
|
|
const [currentProject, setCurrentProject] = useState<string | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
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);
|
|
}
|
|
setLoading(false);
|
|
}, []);
|
|
|
|
const handleSignInSuccess = (newUser: User, token: string) => {
|
|
localStorage.setItem(TOKEN_KEY, token);
|
|
setStoredUser(newUser);
|
|
setUser(newUser);
|
|
socketClient.connect(token);
|
|
};
|
|
|
|
const handleSignOut = () => {
|
|
localStorage.removeItem(TOKEN_KEY);
|
|
localStorage.removeItem(PROJECT_KEY);
|
|
setStoredUser(null);
|
|
setUser(null);
|
|
setCurrentProject(null);
|
|
socketClient.disconnect();
|
|
};
|
|
|
|
const handleSetCurrentProject = (slug: string | null) => {
|
|
setStoredProject(slug);
|
|
setCurrentProject(slug);
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="h-screen flex items-center justify-center bg-bg-primary">
|
|
<div className="text-text-muted">Loading...</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<AppContext.Provider value={{ user, currentProject, setCurrentProject: handleSetCurrentProject, onSignOut: handleSignOut }}>
|
|
<BrowserRouter>
|
|
<div className="h-screen flex flex-col bg-bg-primary">
|
|
<Header user={user} onSignOut={handleSignOut} />
|
|
<main className="flex-1 flex overflow-hidden">
|
|
<Routes>
|
|
<Route path="/" element={<Home user={user} />} />
|
|
<Route path="/projects" element={<ProjectManager user={user} />} />
|
|
<Route path="/projects/new" element={<ProjectManager user={user} />} />
|
|
<Route path="/projects/:slug" element={<ProjectManager user={user} />} />
|
|
<Route path="/drones" element={<DroneManager user={user} />} />
|
|
<Route path="/projects/:projectId/chat-session/:sessionId" element={<ChatSessionView />} />
|
|
<Route
|
|
path="/sign-in"
|
|
element={
|
|
user ? (
|
|
<Navigate to="/" replace />
|
|
) : (
|
|
<SignIn onSuccess={handleSignInSuccess} />
|
|
)
|
|
}
|
|
/>
|
|
<Route
|
|
path="/sign-up"
|
|
element={
|
|
user ? (
|
|
<Navigate to="/" replace />
|
|
) : (
|
|
<SignUp onSuccess={handleSignInSuccess} />
|
|
)
|
|
}
|
|
/>
|
|
</Routes>
|
|
</main>
|
|
<StatusBar projectSlug={currentProject} />
|
|
</div>
|
|
</BrowserRouter>
|
|
</AppContext.Provider>
|
|
);
|
|
} |