import { lazy, Suspense } from 'react'; import { HashRouter, Navigate, Outlet, Route, Routes, useParams } from 'react-router-dom'; import { AppShell } from './components/AppShell'; import { CrashToast } from './components/CrashToast'; import { ErrorBoundary } from './components/ErrorBoundary'; import { RemoteRevokedScreen } from './components/RemoteRevokedScreen'; import { SpinnerIcon } from './components/icons'; import { UpdateToast } from './components/UpdateToast'; import { RequireAdmin, RequireAuth, RequireDevice } from './components/guards'; import { AuthProvider } from './context/AuthContext'; import { CallProvider } from './context/CallContext'; import { ConversationsProvider } from './context/ConversationsContext'; import { FriendshipsProvider } from './context/FriendshipsContext'; import { ThemeProvider } from './context/ThemeContext'; import { AuthPage } from './pages/AuthPage'; import { ChatsEmptyState, ChatsPage } from './pages/ChatsPage'; import { ConversationPage } from './pages/ConversationPage'; // Routes rarely visited on first render are pulled out of the initial bundle. // AuthPage stays eager because it's the first screen unauthenticated users // see; ChatsPage + ConversationPage stay eager because every authenticated // session renders them immediately. const AdminPage = lazy(() => import('./pages/AdminPage').then((m) => ({ default: m.AdminPage }))); const AuthCallbackPage = lazy(() => import('./pages/AuthCallbackPage').then((m) => ({ default: m.AuthCallbackPage })), ); const DevicePage = lazy(() => import('./pages/DevicePage').then((m) => ({ default: m.DevicePage }))); const FriendsPage = lazy(() => import('./pages/FriendsPage').then((m) => ({ default: m.FriendsPage })), ); const SettingsPage = lazy(() => import('./pages/SettingsPage').then((m) => ({ default: m.SettingsPage })), ); const ChangelogPage = lazy(() => import('./pages/ChangelogPage').then((m) => ({ default: m.ChangelogPage })), ); function RouteSuspense({ children }: { children: React.ReactNode }) { return ( } > {children} ); } // Isolates each top-level route so a crash in one page doesn't take the whole // shell down. The boundary auto-retries via `ErrorBoundary`'s backoff schedule. function RouteBoundary({ scope }: { scope: string }) { return ( ); } // Forces a fresh `ConversationPage` instance per `:id` so React unmounts // the previous conversation entirely on switch. Without this, the same // component instance handles every conversation, which leaks state // between chats (messages, scroll position, composer drafts) for one // render frame and gives the "flicker" we're trying to remove. // `useConversationMessages` re-hydrates from `messageMemoryCache` on the // fresh mount so previously-visited chats still render instantly. function ConversationRoute() { const { id } = useParams<{ id: string }>(); return ; } export function App() { return ( }> } /> } /> }> }> } /> }> }> } /> }> }> } /> } /> }> } /> }> } /> }> } /> }> }> } /> } /> ); }