新增点击头像/昵称查看用户资料弹窗,接入消息气泡/@提及/群成员列表

新增全局 UserCardRoot(stores/userCard.ts 驱动,挂载在 AppShell 里,模式同
ConfirmDialogRoot),复用 getUserProfile 数据源,展示头像/昵称/性别地区/签名,
非本人时带发消息/语音/视频快捷操作,底部"查看完整资料"跳转到完整的
/contacts/{id} 页面。

接入点:
- MessageBubble.tsx:群聊和单聊的对方头像/昵称(原来只有群聊头像/昵称是
  Link 到完整资料页,单聊完全没有交互)
- drafty.tsx:@提及实体(Drafty.mention 的 uid 存在 ent[].data.val 里,
  之前渲染完全没读这个字段),"所有人"哨兵值除外
- GroupMembersModal.tsx / GroupDetailColumn.tsx:群成员列表的行内和紧凑
  头像格子

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-09-24 17:45:23 +08:00
co-authored by Claude Sonnet 5
parent 2427a3782c
commit ef31eb2e88
7 changed files with 200 additions and 13 deletions
+23
View File
@@ -0,0 +1,23 @@
import { create } from 'zustand';
/**
* "Tap any avatar/name -> profile popup", app-wide (message bubbles, @mentions inside message text,
* group member rows/grid, etc.) -- one global store + a single `<UserCardRoot>` mounted once at the
* app root (App.tsx), the same shape as `stores/confirmDialog.ts`/`stores/calls.ts`: callable from any
* event handler with no natural "own a piece of JSX for this" home (a Drafty-rendered mention span
* deep inside message content, for one), not a per-component `useState`.
*/
interface UserCardState {
userId: string | null;
}
export const useUserCardStore = create<UserCardState>(() => ({ userId: null }));
export function openUserCard(userId: string) {
if (!userId) return;
useUserCardStore.setState({ userId });
}
export function closeUserCard() {
useUserCardStore.setState({ userId: null });
}