修复codereview复核确认的多选/转发/收藏页/资料页等前端bug

- 多选模式下Drafty链接、名片消息不再意外触发跳转/开新标签页
- 语音/视频消息在多选模式下点击可正常冒泡选中该行
- 转发排除未确认/发送失败消息,识别uncertain转发结果并单独提示
- 群文件"更多操作"菜单竞态、群成员移出状态竞态
- 收藏页链接卡片new URL()崩溃点加防御,并给登录后应用区域加错误边界
- 个人资料页乐观锁版本冲突时清空草稿避免静默覆盖并发修改

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-09-27 16:47:32 +08:00
co-authored by Claude Sonnet 5
parent e3eed8bdd4
commit 9abae5030b
10 changed files with 142 additions and 27 deletions
+27 -1
View File
@@ -1,9 +1,10 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Navigate, createBrowserRouter, RouterProvider } from 'react-router-dom';
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';
@@ -34,6 +35,30 @@ import { SystemMessagesView } from '@/features/system/SystemMessagesView';
const queryClient = new QueryClient();
/**
* Without this, an uncaught render-time exception ANYWHERE under `<AppShell>` (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 (
<div className="flex h-dvh w-full flex-col items-center justify-center gap-3 bg-surface-page px-6 text-center">
<Icon name="error_outline" className="text-[40px] text-text-tertiary-3" />
<p className="text-sm text-text-secondary">页面出现异常,请刷新重试</p>
<button type="button" onClick={() => window.location.reload()} className="rounded-lg bg-accent px-4 py-2 text-sm text-white">
刷新页面
</button>
</div>
);
}
const router = createBrowserRouter([
{ path: 'tenant', element: <TenantEntryPage /> },
{ path: 'login', element: <LoginPage /> },
@@ -45,6 +70,7 @@ const router = createBrowserRouter([
children: [
{
element: <AppShell />,
errorElement: <RouteErrorBoundary />,
children: [
{ index: true, element: <Navigate to="/messages" replace /> },
{