feat(web): complete groups/contacts/favorites/settings/calls/system domains + extensive review-round fixes

Rounds out the full rewrite with the remaining feature domains: group
management (admin panel, mute, files, announcements, QR-based invite/join),
contacts (friend profile, add-friend, blocklist, new-friends), favorites
(real media rendering across text/image/video/voice/file/link), settings
(profile/security/privacy/general/about), calls (Agora-backed audio/video
signaling + call history), and system messages/file assistant.

Also folds in a large batch of live-verified fixes surfaced by repeated
fresh code-review passes and hands-on E2E testing against the real
backend, including: WS reconnect not resubscribing any topic after a
disconnect, missing x-im-client-mid on every send path (protocol
compliance for retry dedup), group mute/member-settings/self-profile
endpoint corrections, relationship-state type fixes across search/QR/
friend-profile, tenant-switch logout gap, password-policy now read from
the real per-tenant config instead of guessed, NavRail highlight/
aria-current desync, several call-state-machine races, and responsive
layout fixes across all four breakpoints (mobile/tablet/laptop/desktop)
including a full mobile rebuild of favorites, call UI, and the settings
home/sub-list split.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-09-09 20:16:33 +08:00
co-authored by Claude Sonnet 5
parent 72b7b79caf
commit 5c3f3b7653
89 changed files with 10158 additions and 442 deletions
+19
View File
@@ -11,6 +11,15 @@ const CTRL_ERROR_TEXT_CN: Record<string, string> = {
'policy violation': '当前操作不符合安全策略要求',
'permission denied': '没有权限执行此操作,请重新登录',
'internal error': '服务器繁忙,请稍后重试',
// Confirmed live 2026-09-09: registering with a username or phone number that's already taken
// returns this exact raw SDK text ("duplicate credential (409)") -- without this entry,
// registerErrorMessage()'s fallback showed the raw English straight to a Chinese-language user.
'duplicate credential': '该用户名或手机号已被注册,请更换后重试',
// docs/IM-前后端对接文档.md's own worked example (`{"login":...,"secret":"其他租户 token"}` ->
// `403 tenant mismatch`) -- without this entry, loginErrorMessage's generic `code===403` fallback
// below shows "账号或密码错误" for what's actually a stale-token-from-a-different-tenant case, which
// is a materially different, more confusing message (the password wasn't wrong at all).
'tenant mismatch': '登录状态异常,请重新进入企业码',
};
function translateCtrlError(err: unknown): string | null {
@@ -20,6 +29,16 @@ function translateCtrlError(err: unknown): string | null {
return CTRL_ERROR_TEXT_CN[normalized] || null;
}
/** docs/IM-前后端对接文档.md: this specific reason means the client is holding a session token
* issued by a DIFFERENT tenant than the one it's currently connected to -- the doc's own prescribed
* recovery is "清 Token 并回企业码页" (clear the token and return to the tenant-code entry screen),
* not just show an error and let the user retry the same broken state. */
export function isTenantMismatch(err: unknown): boolean {
const raw = (err as Error)?.message;
if (!raw) return false;
return raw.replace(/\s*\(\d+\)\s*$/, '').trim().toLowerCase() === 'tenant mismatch';
}
export function loginErrorMessage(err: unknown): string {
const known = translateCtrlError(err);
if (known) return known;