import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { Navigate, createBrowserRouter, RouterProvider, useRouteError } from 'react-router-dom'; import { AppShell } from '@/app/AppShell'; import { ConfirmDialogRoot } from '@/app/ConfirmDialogRoot'; import { GlobalToastRoot } from '@/app/GlobalToastRoot'; import { Icon } from '@/app/Icon'; import { KickedNoticeRoot } from '@/app/KickedNoticeRoot'; import { RequireAuth } from '@/app/RequireAuth'; import { SecurityChallengeModal } from '@/app/SecurityChallengeModal'; import { EnterpriseRegisterPage } from '@/features/auth/EnterpriseRegisterPage'; import { LoginPage } from '@/features/auth/LoginPage'; import { RegisterPage } from '@/features/auth/RegisterPage'; import { ResetPasswordPage } from '@/features/auth/ResetPasswordPage'; import { TenantEntryPage } from '@/features/auth/TenantEntryPage'; import { TenantFeedbackPage } from '@/features/auth/TenantFeedbackPage'; import { CallHistoryView } from '@/features/calls/CallHistoryView'; import { BlocklistView } from '@/features/contacts/BlocklistView'; import { ContactsLayout } from '@/features/contacts/ContactsLayout'; import { EmptyContactsMain } from '@/features/contacts/EmptyContactsMain'; import { FriendProfileView } from '@/features/contacts/FriendProfileView'; import { GroupListView } from '@/features/contacts/GroupListView'; import { NewFriendsView } from '@/features/contacts/NewFriendsView'; import { FavoritesLayout } from '@/features/favorites/FavoritesLayout'; import { ConversationView } from '@/features/messages/ConversationView'; import { EmptyMain } from '@/features/messages/EmptyMain'; import { MessagesLayout } from '@/features/messages/MessagesLayout'; import { GeneralSettingsPage } from '@/features/settings/GeneralSettingsPage'; import { NotificationsSettingsPage } from '@/features/settings/NotificationsSettingsPage'; import { PrivacySettingsPage } from '@/features/settings/PrivacySettingsPage'; import { ProfileSettingsPage } from '@/features/settings/ProfileSettingsPage'; import { SecuritySettingsPage } from '@/features/settings/SecuritySettingsPage'; import { SettingsHome } from '@/features/settings/SettingsHome'; import { SettingsLayout } from '@/features/settings/SettingsLayout'; import { SystemMessagesView } from '@/features/system/SystemMessagesView'; const queryClient = new QueryClient(); /** * Without this, an uncaught render-time exception ANYWHERE under `` (any page, any one * bad field in server data) falls through to react-router's own built-in default error boundary -- * real, not hypothetical (confirmed live: `node_modules/react-router` DOES implicitly wrap the * outermost matched route the way this codebase never configured one to). That default shows * "Unexpected Application Error!" plus the raw error message/stack to the end user and replaces the * WHOLE logged-in shell, sidebar included, with no way back except a full page reload. Attached to * AppShell (not each leaf route) so it covers every authenticated page with one component, and low * enough that `RequireAuth`/login/tenant-entry are never affected by a bug in, say, 收藏. */ function RouteErrorBoundary() { const error = useRouteError(); if (import.meta.env.DEV) console.error(error); return (

页面出现异常,请刷新重试

); } const router = createBrowserRouter([ { path: 'tenant', element: }, { path: 'login', element: }, { path: 'register', element: }, { path: 'reset-password', element: }, { path: 'enterprise-register', element: }, { path: 'feedback', element: }, { element: , children: [ { element: , errorElement: , children: [ { index: true, element: }, { path: 'messages', element: , children: [ { index: true, element: }, { path: 'system', element: }, { path: ':topicId', element: }, ], }, { path: 'contacts', element: , children: [ { index: true, element: }, { path: 'requests', element: }, { path: 'groups', element: }, { path: 'blocklist', element: }, { path: ':userId', element: }, ], }, { path: 'favorites', element: }, // README §9.2's dedicated file-assistant browser (categories, storage quota, upload // progress) has NO backing REST API at all (confirmed via a research pass -- the design // spec's own state-management notes admit reuse of existing APIs, i.e. reading the `slf` // topic's messages, same as any other conversation) -- redirecting into the real // conversation on that topic is honest; a fake quota card with invented numbers isn't. { path: 'files', element: }, { path: 'calls', element: }, { path: 'settings', element: , children: [ { index: true, element: }, { path: 'profile', element: }, { path: 'security', element: }, { path: 'notifications', element: }, { path: 'privacy', element: }, { path: 'general', element: }, ], }, ], }, ], }, // A stale bookmark/link to a route renamed during this redesign should land somewhere the app // itself resolves (tenant/login/messages, per RequireAuth's own gating), not React Router's // generic unstyled default error boundary. { path: '*', element: }, ]); export default function App() { return ( {/* Mounted above the router (not inside AppShell) -- window.confirm()/window.alert() replacements need to work on unauthenticated pages too (e.g. TenantEntryPage's "switch tenant" confirmation), not just the logged-in app tree. */} ); }