fix(web): code review round 1 remaining UI fixes

- EmojiPanel: add missing 手势/物品 categories, 12-column grid per README §3.3
- DetailColumn: add 投诉举报 row, real ID/region/签名 via new getUserProfile
  API, chevron_right icons now use --color-icon-arrow token (were tertiary
  text color), regroup rows so dividers only sit between sections not every
  row (matches design-code reference exactly)
- MessageBubble/ConversationView: right-clicked message bubble now gets the
  spec's outline while its context menu is open
- ChatListColumn/AppShell: pinned 系统消息/文件助手 rows using confirmed
  bootstrap entries/entry_badges topic names ("sys"/"slf"); route to the
  existing SectionPlaceholder pattern since their own dedicated layouts are
  Phase 8, not built yet

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-09-08 03:58:06 +08:00
co-authored by Claude Sonnet 5
parent a7da4dc079
commit 93d4077d1e
9 changed files with 191 additions and 42 deletions
+36 -23
View File
@@ -1,12 +1,29 @@
import { useState } from 'react';
// README §3.3: "表情图位(使用系统 Emoji 或品牌表情包)" -- the design mockup itself has no
// README §3.3: "分类 Tab(最近/表情/手势/物品),12 列网格" -- the design mockup itself has no
// specific emoji asset list, just gray placeholder tiles captioned "use system emoji or a branded
// pack." This is a plain Unicode emoji grid (no custom art), same call the old app made (per its own
// comment: no WeChat/QQ-style sticker assets available, system emoji is the fallback).
const EMOJI = [
const EMOJI_FACES = [
'😀', '😁', '😂', '🤣', '😊', '😍', '😘', '😜', '🤔', '😐', '😴', '😭', '😡', '🥳', '😱', '🤯', '🥰', '😇', '🙃', '😉',
'👍', '👎', '👏', '🙏', '💪', '🤝', '✌️', '👌', '🤞', '👋', '❤️', '💔', '💯', '🔥', '✨', '🎉', '🎂', '☕', '🍺', '🌹',
'😆', '😅', '☺️', '😋', '😝', '🤗', '🤩', '🥺', '😢', '😤', '🤬', '🥵', '🥶', '😵', '🤢', '😷', '🤒', '🥴', '😪', '🙄',
];
const EMOJI_GESTURES = [
'👍', '👎', '👏', '🙏', '💪', '🤝', '✌️', '👌', '🤞', '👋', '👊', '✊', '🤛', '🤜', '🤟', '🤘', '👆', '👇', '👈', '👉',
'☝️', '✋', '🤚', '🖐️', '🖖', '🤙', '💅', '🤳', '👐', '🙌', '🤲', '🫶', '🤌', '🫰', '🫵', '👋', '💃', '🕺', '🙋', '🙆',
];
const EMOJI_OBJECTS = [
'❤️', '💔', '💯', '🔥', '✨', '🎉', '🎂', '☕', '🍺', '🌹', '🎁', '🎈', '⭐', '🌈', '☀️', '🌙', '⚡', '💧', '🎵', '🎮',
'📷', '📱', '💻', '📌', '📎', '🔔', '💰', '⏰', '🔑', '💡', '📖', '✏️', '🍎', '🍕', '🍔', '🍜', '🎄', '🎃', '⚽', '🏆',
];
type EmojiTab = 'recent' | 'faces' | 'gestures' | 'objects';
const TABS: { key: EmojiTab; label: string; list: string[] }[] = [
{ key: 'recent', label: '最近', list: [] },
{ key: 'faces', label: '表情', list: EMOJI_FACES },
{ key: 'gestures', label: '手势', list: EMOJI_GESTURES },
{ key: 'objects', label: '物品', list: EMOJI_OBJECTS },
];
interface EmojiPanelProps {
@@ -14,7 +31,7 @@ interface EmojiPanelProps {
}
export function EmojiPanel({ onPick }: EmojiPanelProps) {
const [tab, setTab] = useState<'recent' | 'all'>('all');
const [tab, setTab] = useState<EmojiTab>('faces');
const recent = (() => {
try {
return JSON.parse(localStorage.getItem('im-recent-emoji') || '[]') as string[];
@@ -24,7 +41,7 @@ export function EmojiPanel({ onPick }: EmojiPanelProps) {
})();
const pick = (emoji: string) => {
const next = [emoji, ...recent.filter((e) => e !== emoji)].slice(0, 30);
const next = [emoji, ...recent.filter((e) => e !== emoji)].slice(0, 36);
try {
localStorage.setItem('im-recent-emoji', JSON.stringify(next));
} catch {
@@ -33,36 +50,32 @@ export function EmojiPanel({ onPick }: EmojiPanelProps) {
onPick(emoji);
};
const list = tab === 'recent' ? recent : EMOJI;
const list = tab === 'recent' ? recent : (TABS.find((t) => t.key === tab)?.list ?? []);
return (
<div className="w-[320px] rounded-xl border border-border-panel bg-white p-2 shadow-menu">
<div className="w-[380px] rounded-xl border border-border-panel bg-white p-2 shadow-menu">
<div className="mb-1 flex gap-1 px-1">
<button
type="button"
onClick={() => setTab('recent')}
className={`rounded-lg px-2.5 py-1 text-xs ${tab === 'recent' ? 'bg-accent-soft-bg text-accent' : 'text-text-tertiary-2'}`}
>
最近
</button>
<button
type="button"
onClick={() => setTab('all')}
className={`rounded-lg px-2.5 py-1 text-xs ${tab === 'all' ? 'bg-accent-soft-bg text-accent' : 'text-text-tertiary-2'}`}
>
表情
</button>
{TABS.map((t) => (
<button
key={t.key}
type="button"
onClick={() => setTab(t.key)}
className={`rounded-lg px-2.5 py-1 text-xs ${tab === t.key ? 'bg-accent-soft-bg text-accent' : 'text-text-tertiary-2'}`}
>
{t.label}
</button>
))}
</div>
{list.length === 0 ? (
<div className="py-6 text-center text-xs text-text-tertiary-4">暂无最近使用</div>
) : (
<div className="grid grid-cols-8 gap-1">
<div className="grid grid-cols-12 gap-1">
{list.map((emoji, i) => (
<button
key={`${emoji}-${i}`}
type="button"
onClick={() => pick(emoji)}
className="grid h-9 w-9 place-items-center rounded-lg text-xl hover:bg-surface-rail"
className="grid h-8 w-8 place-items-center rounded-lg text-lg hover:bg-surface-rail"
>
{emoji}
</button>