Files
MailUI4Agents/client/electron
JianFeeeee 4ff6b10260 跨端: 联系人页补齐「写信 / 归档」—— 顺带撞出三个只在跑起来才现形的 bug
用户:「你自己看看这些页面和webui有哪怕一丁点的相似之处吗?」
把两个客户端**同一个宽度**并排看之后,缺的很具体:WebUI 联系人卡片有
「写信 / 归档」,鸿蒙一个都没有。补的过程撞出三个缺陷,都不是"代码不合法"——
编译器与既有判据全绿:

## ① 归档打的是**服务端不存在**的路由(死函数)

`MailApi.archiveContact` 打的是 `DELETE /me/contacts/{name}/{path}`:

- `grep 'me/contacts' server/cmd/server/main.go` **零命中** ⇒ 按钮接上去就是 404;
- 全仓**没有任何调用方** ⇒ 它是个从没跑过的死函数,所以"没有归档按钮"这件事
  一直没暴露这个错。

服务端真实形状是 `POST /api/v1/contacts/archive`(`handler.ArchiveContact`),
body 二选一 `{session_id}` / `{address}`。**跟 WebUI 同口径用 session_id**:
address 会随别名变化,只有 session_id 是会话的身份。

## ② 用已存 token 恢复登录**永远进不去主界面**(最恶劣的一个)

`LoginPage.tryRestore` 验完 token 就结束了 —— 设了 `loggedIn = true`
(界面出现「登录成功:jianf」)却**从无跳转**。本文件另外两条成功路径
(`aboutToAppear` 快速路径、`doLogin` 末尾)都有跳转,唯独这条没有,
而它**恰恰是老用户最常走的那条**(重启时 token 还在,`doLogin` 根本不会被调用)。

症状最坏的地方是它**看起来是成功的**。实测(模拟器 + jianf 的永久 key):
日志只有 `→ GET /auth/me` 然后什么都没有。补齐 ① 跳转 ② 账号登记
③ SSE 连接(后两条是 `doLogin` 有而这里缺的,少了它们进主界面是个瘸的状态,
而且因为 `aboutToAppear` 的快速路径靠账号命中,下次启动还会重走这里 ⇒ 永远进不去)。

修后实测:启动即进主界面(可见文本变成「发件箱/授权/收件箱 · 2 组 · 50 封」)。

## ③ 时间戳原样印出来

卡片直接印 `last_activity` ⇒ 屏上是 `2026-09-18T02:50:35.49065Z`。
WebUI 是 `09/18 10:50`(`toLocaleString('zh-CN', {month,day,hour,minute})`,**本地**时区)。
加 `MailGrouping.shortTimeOf`(走 `Date` 取本地字段;**不许 `.slice()`** ——
那是拿 UTC 的月/日当本地时刻,UTC+8 的 09-15 00:30 会显示成 09-14 16:30)。
实测:`112 封 · 09/18 10:50`,与 WebUI 逐字一致。

## 补的界面(三折叠模拟器 3184px 展开态实测)

- 两种视图**各一处**「写信 / 归档」(WebUI 两视图同语义),WebUI 用 `.reveal`
  悬停显形,**鸿蒙不能照抄**:`index.css` 那段注释已经踩过这个坑
  (触摸设备没 hover ⇒ 按钮透明却仍可点,一个看不见却按得动的破坏性按钮更糟),
  WebUI 的修法是只在真支持悬停的设备上隐藏 ⇒ 鸿蒙这两个按钮**常显**。
- 归档先确认,**两视图共用同一个确认框**(WebUI 原话:换个视图就换套确认 UI
  只会让人对「自己点了什么」更没底);文案逐字一致。
- 列表视图的 meta 行原来放 `last_preview`,于是同一联系人在两视图里的关键信息
  不一致 ⇒ 改成与卡片视图同源(`N 封 · 时间`)。
- ★ 一处只有跑起来才会发现的坑:列表视图的 ListItem 钉死 `.height(85)` +
  `clip(true)`,而确认框比 85 高 ⇒ **按钮被裁掉、点不了也退不出**。
  确认态下高度交给内容自己定。

## 判据(新增 harmony-contacts,5 条;4 个变异方向都跑过)

判的都是"按下去会发生什么",不是"按钮在不在":
① 归档打的是服务端真有的路由(同时钉住**没有**再用那条不存在的 DELETE —— 只钉前者的话,
   加个平行实现也能全绿);② 两视图各一处动作且去向正确;③ 确认框只有 1 个定义、
   两视图都调它、文案逐字一致、确认态下点卡片不开会话;④ 时间戳走了格式化且
   函数本身不走 slice;⑤ **切出 `tryRestore` 的函数体**判它自己含跳转
   (只判全文件出现次数的话,另外两条路径里那两句就够让它变绿)。
2026-09-18 12:17:41 +08:00
..