跨端: 联系人页补齐「写信 / 归档」—— 顺带撞出三个只在跑起来才现形的 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` 的函数体**判它自己含跳转
(只判全文件出现次数的话,另外两条路径里那两句就够让它变绿)。
This commit is contained in:
@ -72,6 +72,27 @@ struct LoginPage {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用**已存下来的 token** 恢复登录(启动时 `init()` 读到 token 就走这里)。
|
||||
*
|
||||
* ★★ 2026-09-18 修:原来这个方法**验完 token 就结束了** —— 它设了 `loggedIn = true`
|
||||
* (于是界面上出现「登录成功:jianf」)却**从不跳转主界面**。
|
||||
* 症状最恶劣的地方在于它**看起来是成功的**:用户看到"登录成功"四个字,
|
||||
* 然后永远停在登录页,再点登录又会重新走一遍。实测(模拟器、jianf 的永久 key):
|
||||
* 日志只有 `→ GET /auth/me` 然后什么都没有。
|
||||
*
|
||||
* 本文件里另外两条成功路径都有跳转(`aboutToAppear` 的快速路径、
|
||||
* `doLogin` 末尾),唯独这条没有 —— 而这条**恰恰是老用户最常走的那条**
|
||||
* (重启 App 时 token 还在,`doLogin` 根本不会被调用)。
|
||||
*
|
||||
* 顺带补齐 `doLogin` 有而这里缺的三件事,否则"恢复登录"进主界面之后
|
||||
* 是个瘸的状态:
|
||||
* ① 写进多账号管理器(少了它,设置页/账号切换看不到这个账号,
|
||||
* 而 `aboutToAppear` 的快速路径正是靠它命中的 ⇒ 下次启动又会重走这里);
|
||||
* ② 为账号建 SSE(少了它,主界面收不到实时事件);
|
||||
* ③ 跳转主界面。
|
||||
* 推送 token 的补报本来就有(下一轮起 `reportPushToken` 会命中已报过的缓存)。
|
||||
*/
|
||||
async tryRestore(token: string): Promise<void> {
|
||||
this.loading = true;
|
||||
try {
|
||||
@ -82,7 +103,19 @@ struct LoginPage {
|
||||
const user = await new AuthApi(c).loginWithKey(token);
|
||||
this.me = user;
|
||||
this.loggedIn = true;
|
||||
// ① 登记账号 + ② 建 SSE(与 doLogin 同一套)
|
||||
const ctx: Context | undefined = this.getUIContext().getHostContext();
|
||||
if (ctx !== undefined) {
|
||||
const acctMgr: AccountManager = AccountManager.getInstance(ctx);
|
||||
await acctMgr.load();
|
||||
const server: string = c.getBase();
|
||||
const acct = await acctMgr.addAccount(server, user.username, token,
|
||||
user.display_name.length > 0 ? user.display_name : user.username);
|
||||
SseService.getInstance().connectForAccount(acct.id, acct.server, acct.token);
|
||||
}
|
||||
this.reportPushToken(c);
|
||||
// ③ ★ 跳转 —— 原来缺的就是这一句
|
||||
await this.getUIContext().getRouter().pushUrl({ url: 'pages/MainPage' });
|
||||
} catch (e) {
|
||||
const c2: ApiClient | null = this.client;
|
||||
if (c2 !== null) {
|
||||
|
||||
Reference in New Issue
Block a user