Files
ts-mobile-go/docs/TS Mobile UI UX 重构设计规范.md
sansenandClaude Fable 5 8716b391f6 工程改进:跨语言契约固化、日志脱敏、状态模型收敛
依据 docs/项目工程改进方案.md 实施(第五梯队 CI/协作除外)。

安全性
- Release 剥离 Log.v/d/i/w(-assumenosideeffects),保留 Log.e
- 全量频道/成员 dump、消息正文、发送者 UID 改为 BuildConfig.DEBUG 保护
  (实测 release dex 中 "Channel: id=" / "Client: id=" / selfId= 等均为 0 命中)

构建产物体积
- .gitignore 补 *.a(保留 .opus/lib 四个 ABI 预编译库)、.opus/install、
  app/libs/*.aar、dnn/torch
- git rm --cached 除名 AAR、.gradle、.opus/{build,install}、dnn/torch
  跟踪体积 50MB → 17.4MB,工作区文件不受影响

跨语言契约(本方案核心)
- 新增 go/teamspeak/contract.go:字段名常量 + BridgeContractVersion 单一事实来源
- 新增契约测试:反射断言常量与 struct tag 一致;Go/Kotlin 共用 golden 样本
- 新增 BridgeContract.kt:启动校验 AAR 契约版本,不匹配则阻止连接
- 修正文档:channel_order 是前驱频道 ID(链表指针)而非排序权重,
  按它数值排序会打乱频道树;ChannelInfo.order 在 Go 侧不存在

状态模型
- ConnectionState 增 Idle 取代 null 编码,connectionState 不再可空
- applyClients 改为按频道差分更新,避免全量刷新导致频道树整体重组
- 频道数据 freshness 统一由 Repository 维护,修复 ViewModel 与 Repository
  两份时间戳不同步导致的重复刷新

UI
- 修复 collectAsState() 在参数位置调用导致的 isSelf 快照失效
- 消除 9 处 !! 断言(ChannelListScreen)
- 硬编码 24.5/14.5dp 与 depth*24 收敛到 UiTokens.Spacing,缩进加 4 层上限

测试
- Kotlin 测试 7 → 106;Go 契约测试新增 24 个用例
- 各覆盖 InputValidator、消息送达确认、僵尸会话过滤、频道差分、
  频道顺序语义、错误分类

其他修复
- classifyError 提取为纯函数并补测试;修复 too many clones (id=521)
  未识别导致英文原文直接暴露给用户

版本号提升至 1.0.13(versionCode 13)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-10 14:58:15 +08:00

23 KiB
Raw Permalink Blame History

TS Mobile UI / UX 重构设计规范

1. 文档目标

本文档用于指导 TS Mobile Android 客户端下一阶段 UI / UX 重构。

重构目标不是简单地:

  • 修改颜色
  • 增加圆角
  • 增加动画
  • 更换 Material 组件
  • 美化现有页面

而是重新建立一套符合 TeamSpeak 使用模型的移动端信息架构。

核心目标:

让用户始终清楚自己连接到了哪个服务器、当前处于哪个频道、频道里有哪些成员,以及当前语音和聊天状态。


2. 核心产品模型

TeamSpeak 与普通聊天软件最大的区别之一,是:

用户连接服务器后,会直接处于某个频道上下文中。

因此 TS Mobile 不应该建立:

服务器
  ↓
进入频道

这样的用户流程。

正确模型:

未连接
  ↓
连接服务器
  ↓
已连接 + 已进入频道
  ↓
服务器工作空间

连接成功后:

Server
└── Current Channel
    ├── Chat
    ├── Members
    └── Voice

因此:

Current Channel 是整个 App 最重要的上下文状态。


3. 顶层状态模型

建议 UI 层只暴露三个主要状态:

Disconnected
    ↓
Connecting
    ↓
Connected

其中:

Disconnected

没有连接服务器。

显示:

  • Logo
  • Server Address
  • Nickname
  • Password
  • Recent Servers
  • Connect

Connecting

连接服务器过程中。

这是连接流程中的 Loading 状态,而不是独立产品页面。

可以显示:

Connecting...

server.example.com:9987

Connected

连接成功后直接进入当前频道。

Server
Current Channel
Chat / Channels
Voice
Members

不应该存在:

Connected but no channel

这样的普通 UI 状态。


4. 产品信息架构

最终信息架构:

TS Mobile
│
├── Connection
│   ├── Connect
│   ├── Connecting
│   └── Disconnected
│
└── Server Space
    │
    ├── Server Header
    │
    ├── Current Channel
    │
    ├── Chat
    │
    ├── Channels
    │   ├── Channel Tree
    │   └── Channel Members
    │
    ├── Voice
    │
    └── Server Drawer
        ├── Server Info
        ├── Voice Settings
        ├── Notifications
        ├── Appearance
        └── Disconnect

5. Server Space

连接成功后,整个 App 进入一个统一的:

Server Space

而不是分别进入:

ChannelScreen
ChatScreen
VoiceScreen

这些功能都属于当前 Server Space。

推荐结构:

┌────────────────────────────┐
│ Server Header              │
├────────────────────────────┤
│                            │
│                            │
│      Current Content       │
│                            │
│                            │
├────────────────────────────┤
│ Current Channel            │
├────────────────────────────┤
│ Chat             Channels  │
└────────────────────────────┘

6. Server Header

Server Header 负责展示服务器上下文。

推荐:

┌────────────────────────────┐
│ ☰  My Server            ⋮ │
│    ● Connected · 12 online│
└────────────────────────────┘

包含:

  • Server Name
  • Connection Status
  • Online Count
  • Navigation / Drawer
  • More

不建议长期显示:

server.example.com:9987

服务器地址属于 Server Detail。


7. Current Channel Context Bar

这是本次重构最重要的 UI 组件之一。

它不是普通 Card。

它是:

当前通信上下文。

推荐:

┌────────────────────────────┐
│ # General             12  │
│ ● ● ● ● +8                │
└────────────────────────────┘

显示:

  • Channel Name
  • Channel Type
  • Member Count
  • 少量成员头像
  • 当前语音状态

例如:

# General
12 members
● ● ● ● +8

用户切换频道后:

# General

直接变成:

# Gaming

同时:

  • Chat 改变
  • Members 改变
  • Voice Context 改变

8. Chat / Channels 导航

推荐保留底部双 Tab:

┌────────────────────────────┐
│                            │
│       Main Content         │
│                            │
├────────────────────────────┤
│ # General        12        │
├────────────────────────────┤
│    Chat          Channels  │
└────────────────────────────┘

但需要明确:

Chat / Channels 是视图,不是产品实体。

真正的实体关系:

Server
└── Current Channel
      ├── Chat
      └── Members / Channel Tree

9. Chat

Chat 是当前频道的文字消息流。

推荐布局:

┌────────────────────────────┐
│ # General             12  │
├────────────────────────────┤
│                            │
│ Alex                       │
│ Hello                      │
│                            │
│ Tom                        │
│ Anyone playing?            │
│                            │
│ Alex                       │
│ Let's go                   │
│                            │
├────────────────────────────┤
│ +  Message...          ↑  │
└────────────────────────────┘

10. Chat 消息设计

不建议过度模仿:

  • WhatsApp
  • Telegram
  • Discord

TeamSpeak 更适合:

紧凑型实时频道消息流。

连续消息建议合并:

Alex
Hello
Anyone playing?
Let's go

而不是:

Alex  Hello
Alex  Anyone playing?
Alex  Let's go

这样可以明显减少垂直空间消耗。


11. Chat 输入框

推荐:

┌────────────────────────────┐
│ +  Message...          ↑  │
└────────────────────────────┘

输入区域保持较大的触控高度。

不要把:

  • Emoji
  • 文件
  • 图片
  • 语音
  • 更多
  • 格式化

全部放到一级 UI。

TeamSpeak 的文字聊天不是核心功能。


12. Channels

Channels 页面负责:

浏览服务器结构 + 查看成员。

推荐:

CHANNELS

▼ Lobby                         5
    ● Alice
    ● Bob

▼ General                     12
    ● Tom
    ● Alex
    ● Mike

▶ Gaming                       8

▶ Music                        3

▶ AFK                           2

频道节点应该保持紧凑。


13. Channel Item

频道 Item 推荐包含:

[展开] [频道类型] [名称] [人数]

例如:

▼  🎙 General                 12

避免在频道 Item 内塞入过多状态。

详细信息通过:

Channel Detail

查看。


14. Current Channel 与 Channel Tree 的关系

频道树中的当前频道应该具有明显但克制的选中状态。

例如:

▶ Lobby

▼ General                 12
   ● Alex
   ● Tom

▶ Gaming                   8

当前频道:

General

可以使用:

  • Surface 提升
  • Primary 色边
  • Icon 高亮

但不要使用非常强烈的纯色块。


15. Member Item

成员列表推荐:

[Avatar] Alex
         ● Online

如果正在说话:

[Avatar] Alex
         ● Speaking

但正常状态不要一直显示文字。

推荐使用:

Avatar
+
Status Indicator
+
Nickname

16. Member 状态系统

至少定义:

Online
Idle
Away
Muted
Deafened
Speaking
Server Muted
Disconnected

其中:

Speaking

是最重要的实时状态。

可以通过:

  • Avatar Ring
  • 状态点
  • 小型音量指示器

表达。

避免大面积动态动画。


17. Member Action

成员操作统一进入 Action Menu。

推荐:

Alex

Interaction
    Poke

Information
    Copy Nickname
    Copy UID

Management
    Move
    Kick from Channel
    Kick from Server

危险操作单独处理。

例如:

Kick from Server

需要二次确认。


18. Voice

Voice 不应该成为独立一级页面。

它属于:

Current Channel
└── Voice

因此:

Current Channel = General

意味着:

Voice Context = General

切换:

General → Gaming

Voice Context 同时切换。


19. Voice UI

推荐将语音设计成一个状态组件。

Connected

┌────────────────────────────┐
│ 🎙 General             12 │
│ ● Connected                │
│                            │
│          [ PTT ]           │
│                            │
│ Mic                 Speaker│
└────────────────────────────┘

Speaking

┌────────────────────────────┐
│ 🎙 General             12 │
│                            │
│       [ SPEAKING ]         │
│                            │
└────────────────────────────┘

PTT 是语音 UI 的核心操作。


20. PTT 状态

PTT 至少存在:

Idle
 ↓
Pressed
 ↓
Speaking

反馈必须:

  • 快
  • 明确
  • 稳定
  • 低干扰

不建议加入复杂粒子效果或大面积动画。


21. Server Drawer

Server Drawer 的职责:

管理当前服务器。

不是简单的服务器详情卡。

推荐:

┌────────────────────────────┐
│ Server                     │
│                            │
│       My Server            │
│       ● Connected          │
│                            │
├────────────────────────────┤
│ Server Info                │
│ server.example.com:9987    │
│ 12 / 64 online             │
├────────────────────────────┤
│ Voice                      │
│ Notifications              │
│ Appearance                 │
│ Settings                   │
├────────────────────────────┤
│                            │
│ Disconnect                 │
└────────────────────────────┘

22. Server Detail

Server Detail 可以展示:

Server Name
Address
Port
Version
Online Users
Max Users
Connection Quality

但这些信息不应该占用主界面。


23. Connection Screen

连接页是唯一真正意义上的“未连接 UI”。

推荐:

TS Mobile

Connect to your TeamSpeak server

Server address
┌────────────────────────────┐
│ server.example.com         │
└────────────────────────────┘

Port
┌────────────────────────────┐
│ 9987                       │
└────────────────────────────┘

Nickname
┌────────────────────────────┐
│ Player                     │
└────────────────────────────┘

        [ Connect ]

Recent Servers

24. Recent Servers

如果存在历史连接:

Recent Servers

┌────────────────────────────┐
│ My Server                  │
│ server.example.com:9987    │
│                         ›  │
└────────────────────────────┘

┌────────────────────────────┐
│ Community Server           │
│ ts.example.net:9987        │
│                         ›  │
└────────────────────────────┘

最近连接应该成为高频入口。

首次使用才重点展示完整 Connect Form。


25. Connecting

Connecting 不建议做成长期存在的独立页面。

推荐:

TS Mobile

        ◯

Connecting...

server.example.com:9987

        Cancel

连接完成:

直接进入 Server Space

而不是:

Connecting
→ Connected
→ Select Channel

26. Disconnect

Disconnect 后:

Server Space
     ↓
Disconnected
     ↓
Connection Screen

重新连接:

Connect
     ↓
Connecting
     ↓
Current Channel

27. Channel Switch

用户点击频道:

General

切换:

Gaming

本质上只是:

currentChannelId

发生变化。

UI 同步更新:

Current Channel
Chat
Members
Voice

不应该把它理解为:

Navigation → New Page

而应该是:

Context → New Channel

28. Bottom Sheet / Dialog / Drawer 职责

项目中已经存在多种 Sheet、Dialog、Card。

需要建立明确规则。

Page

用于主要任务。

Connect
Chat
Channels

Drawer

用于全局导航和 Server 控制。

Server
Settings
Disconnect

Bottom Sheet

用于当前上下文详情。

Channel Detail
Member Detail
Voice Detail

Dialog

用于:

确认
危险操作
输入

避免出现:

BottomSheet
    ↓
Dialog
    ↓
BottomSheet

这种复杂交互链。


29. Design Token

建议继续强化现有:

Color
Shapes
Theme
Type
UiTokens

体系。

统一定义:

Spacing

xs  = 4dp
sm  = 8dp
md  = 12dp
lg  = 16dp
xl  = 24dp

30. Radius

建议:

List Item       8~12dp
Card            12~16dp
Floating Card   16~20dp
Bottom Sheet    24dp
Avatar          Circle

不要所有组件统一 24dp。

圆角本身应该承担层级表达作用。


31. 控件尺寸

建议建立固定 Control Token:

Small Button
40dp

Normal Button
48dp

Input
52dp

List Item
52~56dp

Avatar Small
32dp

Avatar Medium
40dp

Avatar Large
56dp

避免大量:

14.5dp
17dp
24.5dp

这种局部修正值。

如果确实需要特殊值,应有明确视觉原因。


32. Typography

推荐建立:

Title
16~20sp / Bold

Body
14~16sp / Regular

Secondary
12~14sp / Regular

Caption
11~12sp / Regular

移动端不要大量使用过小文字。

尤其:

  • Channel Name
  • Nickname
  • Message

必须优先保证可读性。


33. Semantic Colors

不要让每个组件自己定义颜色。

统一语义:

Primary
Accent
Success
Warning
Error
Muted
Disabled
Speaking

例如:

Connected
→ Success

Speaking
→ Accent

Reconnecting
→ Warning

Disconnected
→ Error

34. 状态视觉规范

状态优先级:

Connected
    ↓
Current Channel
    ↓
Speaking
    ↓
Other User States

越重要的状态,视觉反馈越明显。

但不要同时让多个状态都高亮。


35. 动画规范

动画不应该成为 UI 主体。

优先:

状态变化动画

而不是:

页面切换动画

推荐:

  • Channel selected
  • Speaking
  • PTT pressed
  • Connection state
  • Reconnect banner

使用短时、低幅度动画。


36. 视觉风格

最终建议采用:

Modern
Flat
Native
Compact
Readable
Low Decoration

不是:

Discord Clone

也不是:

Material 3 Demo

而是:

Modern Native Communication Client


37. 推荐视觉层级

整体控制为:

Background
    ↓
Surface
    ↓
Elevated Surface
    ↓
Primary Action

例如:

Background
    页面背景

Surface
    Chat / Channel List

Elevated Surface
    Current Channel / Voice

Primary
    Connect / PTT / Send

不要给每一个组件增加独立背景。


38. 主页面最终结构

最终推荐:

┌─────────────────────────────┐
│ ☰  My Server             ⋮ │
│    ● Connected · 12 online │
├─────────────────────────────┤
│                             │
│  # General             12  │
│  ● ● ● ● +8                │
│                             │
├─────────────────────────────┤
│                             │
│                             │
│       Chat Content          │
│                             │
│                             │
├─────────────────────────────┤
│ +  Message...           ↑  │
├─────────────────────────────┤
│       Chat       Channels  │
└─────────────────────────────┘

Channels:

┌─────────────────────────────┐
│ ☰  My Server             ⋮ │
│    ● Connected · 12 online │
├─────────────────────────────┤
│ # General             12  │
├─────────────────────────────┤
│ CHANNELS                    │
│                             │
│ ▼ Lobby                 5  │
│     ● Alice                 │
│     ● Bob                   │
│                             │
│ ▼ General              12  │
│     ● Alex                  │
│     ● Tom                   │
│                             │
│ ▶ Gaming                8  │
│ ▶ Music                 3  │
│ ▶ AFK                   2  │
├─────────────────────────────┤
│       Chat       Channels  │
└─────────────────────────────┘

39. UI 组件层级

建议最终组件结构:

App
│
└── ServerSpace
    │
    ├── ServerHeader
    │
    ├── CurrentChannelBar
    │
    ├── Content
    │   ├── ChatView
    │   │   ├── MessageList
    │   │   └── MessageInput
    │   │
    │   └── ChannelsView
    │       ├── ChannelTree
    │       └── MemberList
    │
    ├── VoiceStatus
    │
    ├── BottomNavigation
    │
    └── ServerDrawer

40. 推荐代码重构方向

现有代码已经有:

AppTopBar
MessageInputBar
VoiceCard
VoiceControlBar
ServerDetailCard
ClientActionMenu

下一阶段建议进一步抽象为:

ui/components/
│
├── server/
│   ├── ServerHeader
│   ├── ServerDrawer
│   └── ServerStatus
│
├── channel/
│   ├── CurrentChannelBar
│   ├── ChannelTree
│   ├── ChannelItem
│   └── ChannelDetail
│
├── client/
│   ├── ClientItem
│   ├── ClientAvatar
│   ├── ClientStatus
│   └── ClientActionMenu
│
├── chat/
│   ├── MessageList
│   ├── MessageItem
│   └── MessageInput
│
└── voice/
    ├── VoiceStatus
    ├── PTTButton
    └── VoiceControl

这样比继续按照 Screen 切组件更加容易维护。


41. ViewModel 状态建议

UI 最终应该围绕一个核心 Server Space 状态:

ServerSpaceState

connectionState

server
currentChannel

channels
clients

chatMessages

voiceState

其中:

currentChannel

是核心状态。

UI 不应该自己维护多个互相独立的:

selectedChannel
voiceChannel
chatChannel
memberChannel

否则很容易出现状态不同步。

应该:

currentChannelId

作为统一来源。


42. P0 重构优先级

P0-1

重新定义 Server Space。

目标:

Connected
    ↓
Current Channel

作为整个 App 的核心模型。


P0-2

重构:

CurrentChannelBar

让它成为全局 Context Bar。


P0-3

重新整理:

Chat
Channels

两个 Tab 的关系。


P0-4

重构:

Voice
PTT
Voice Status

让它们绑定 Current Channel。


P0-5

统一:

Server Header
Server Drawer

43. P1

完成:

Channel Tree
Member Item
Member Detail
Channel Detail
Recent Servers
Server Settings

并统一 Sheet / Dialog / Drawer。


44. P2

最后处理:

Animation
Gesture
Motion
Micro Interaction
Advanced Voice Feedback

45. 最终设计原则

TS Mobile 的 UI 应该始终遵循以下五条原则:

1. Server 是工作空间

连接服务器以后,用户进入 Server Space。

2. Current Channel 是核心上下文

所有通信行为围绕当前频道。

3. Chat / Channels 是视图

它们不是两个独立产品页面。

4. Voice / Members / Chat 都绑定 Current Channel

切换频道意味着整个通信上下文切换。

5. UI 优先表达状态,而不是装饰

用户第一眼应该知道:

Server
Current Channel
Members
Voice State
Chat

而不是看到:

圆角
阴影
动画
渐变
装饰

46. 最终目标

最终用户打开 TS Mobile 后:

第一次使用
    ↓
连接服务器
    ↓
直接进入服务器默认 / 当前频道
    ↓
看到当前频道
    ↓
可以聊天
    ↓
可以查看频道树
    ↓
可以看到成员
    ↓
可以进行语音通信

整个过程中不应该出现多余的:

服务器主页
选择频道
进入频道
频道主页
聊天主页
语音主页

而应该保持一个稳定的模型:

Server
  +
Current Channel
  +
Current View

其中:

Current View =
Chat
or
Channels

这是 TS Mobile 最适合移动端的 UI / UX 核心架构。


47. 一句话定义

如果最终需要给这个 UI 架构定一句设计原则:

TS Mobile 不是“服务器列表 + 聊天 + 频道 + 语音”的功能集合,而是一个以 Server 为工作空间、以 Current Channel 为核心上下文的移动 TeamSpeak 客户端。