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 (
}>
} />
}
/>
}>
}>
}
/>
}>
}>
} />
}>
}>
} />
}
/>
}>
}
/>
}>
}
/>
}>
}
/>
}>
}>
}
/>
} />
);
}