跨端: 接上聚合失败横幅(数据一直在收集,界面从来没读过 —— 安全网是断的)

逐页对齐 WebUI 时发现的**不是观感问题,是安全网断线**。

## 事实

`MailStore` 一直在收集"聚合模式下哪个账号取失败了":

    MailStore.ets:142  const failed: AccountError[] = [];
    MailStore.ets:208  failed.push(AccountError.of(acct.displayName, reason));
    MailStore.ets:226  snap.accountErrors = failed;
    (收件箱与发件箱两处 load 都写)

而**界面一次都没读过 `snap.accountErrors`**(`MainPage` 里 grep 为 0 处)。

⇒ 后果:某个账号拉不到邮件时,列表**静默少一整份**,界面看起来完全正常。
   用户会据此得出错误结论 —— "没人给我发信"。

WebUI 把这条写成了显式警告(`MailList.tsx:85-91` 原注释):

  「聚合时**某个账号取不到**必须说出来:静默丢掉它,列表会少一整份邮件,
    而界面看起来完全正常 —— 这正是『聚合』最容易骗人的失败方式」

## 改动

· `InboxTab` 加 `@State accountErrors`,在 `applyStoreSnapshot` 里接上 `snap.accountErrors`;
· 卡片下方渲染琥珀色横幅,文案照 WebUI:
  「有 {n} 个账号没取到:{账号}({原因});…」
  (`bg-amber-50 border-amber-200 text-amber-800` → `warnBg` / `warnFgDark` / 11 号字);
· 新增 `accountErrorText()` 做拼接(`「a(原因);b(原因)」`)。

★ 只接了**收件箱**那一处。发件箱 `SentTab` 有自己的 `applyStoreSnapshot`,
  本轮不动它 —— 它的聚合语义与收件箱不同(见 `SentTab.load`),
  要不要显示同一张横幅需要单独判断,不顺手加(顺手加是本仓反复出现的错法)。

## 设备验证(如实)

✓ 收件箱正常渲染(`9 组 · 9 封`),进程存活(22242),无新 jscrash
✓ 横幅**正确不出现** —— 当前所有账号都取得到(这是应该的行为)
✗ **横幅"出现"的那条路径没能实测到**:要触发它需要一个取失败的账号,
  而本机没有可用的失败账号构造。逻辑与 WebUI 逐条对齐、编译通过,
  但"真的会显示成那样"我不声称已验 —— 与"动画中间帧看不到"同样的诚实口径。
This commit is contained in:
2026-09-21 16:32:49 +08:00
parent 877fda5829
commit 1869924507

View File

@ -18,7 +18,7 @@ import { MailApi, InboxResponse } from '../api/MailApi';
import { AccountManager, AccountInfo } from '../api/AccountManager';
import { SseService, SseEvent } from '../api/SseService';
import { AppearanceStore } from '../common/AppearanceStore';
import { MailStore, MailSnapshot, INBOX_PAGE_SIZE } from '../common/MailStore';
import { MailStore, MailSnapshot, AccountError, INBOX_PAGE_SIZE } from '../common/MailStore';
import { AppearanceApi } from '../api/AppearanceApi';
import { performLogout } from '../api/Logout';
import { ComposeIntent } from '../common/ComposeIntent';
@ -502,6 +502,26 @@ struct InboxTab {
}
/** 把 store 快照接进本组件的 `@State`(触发 ArkUI 刷新)。 */
/**
* 聚合模式下**单个账号**取失败的原因(`MailStore` 已在收集,此前没人读)。
*
* ★★ 2026-09-21 补(逐页对齐 WebUI 时发现的安全网断线):
* 数据一直在 `MailSnapshot.accountErrors` 里、两处 load 都写,
* 但**界面从来没有读过它** ⇒ 某个账号拉不到邮件时列表**静默少一整份**,
* 而界面看起来完全正常 —— 用户会得出错误结论("没人给我发信")。
*/
@State accountErrors: AccountError[] = [];
/** 把聚合失败拼成 WebUI 那句文案:「a(原因);b(原因)」 */
private accountErrorText(): string {
const parts: string[] = [];
for (let i = 0; i < this.accountErrors.length; i++) {
const e: AccountError = this.accountErrors[i];
parts.push(e.reason.length > 0 ? e.account + '(' + e.reason + ')' : e.account);
}
return parts.join(';');
}
private applyStoreSnapshot(snap: MailSnapshot): void {
/*
* `MailLike[]` → `MailSummary[]`:store 持有的是接口类型(它不该依赖具体实现类),
@ -516,6 +536,8 @@ struct InboxTab {
this.notice = snap.notice;
this.loading = snap.loading;
this.error = snap.error;
/* 聚合失败原因(见 `accountErrors` 的注释:数据一直有,界面前面没接) */
this.accountErrors = snap.accountErrors;
}
/** 展开状态放在数组里(ArkTS 的 @State 对 Map/Set 的变更不总是能观察到) */
@ -613,6 +635,31 @@ struct InboxTab {
.padding({ left: 10, right: 10 })
.margin({ top: 10 })
/*
* 聚合失败横幅(对齐 WebUI `MailList.tsx:85-96` 的 `account-errors`)。
*
* 文案/形状照 WebUI:
* 「有 {n} 个账号没取到:{账号}({原因});…」
* 琥珀底 + 琥珀边 + 11 号字(`bg-amber-50 border-amber-200 text-amber-800`)。
*
* ★★ 为什么值得单独占一块地方(WebUI 原注释):
* 「聚合时**某个账号取不到**必须说出来:静默丢掉它,列表会少一整份邮件,
* 而界面看起来完全正常 —— 这正是"聚合"最容易骗人的失败方式」
*
* 本仓的情况更糟一点:`MailStore` **一直在收集**这些失败
* (`snap.accountErrors`,两处 load 都写),但**界面从来没有读过它** ⇒
* 这个安全网当前是断的。是逐页对齐时比对出来的,不是猜的。
*/
if (this.accountErrors.length > 0) {
Text('有 ' + this.accountErrors.length + ' 个账号没取到:' + this.accountErrorText())
.fontSize(11).fontColor(Theme.warnFgDark)
.width('100%')
.padding({ left: 8, right: 8, top: 6, bottom: 6 })
.backgroundColor(Theme.warnBg)
.borderRadius(6)
.margin({ top: 6 })
}
// 账号选择器下拉
if (this.showAccountPicker && this.accountList.length > 1) {
Column() {