From b31784010c86380c74db02c8bcaec6c62085dc18 Mon Sep 17 00:00:00 2001 From: Zhou Weidong <502333560@qq.com> Date: Wed, 21 Oct 2026 17:54:29 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E8=AE=B0=E5=BD=95=20K95.1=20=E9=A6=96?= =?UTF-8?q?=E6=AC=A1=E5=88=87=E6=8D=A2=E6=97=A0=E6=84=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-10-21-K95.1-seamless-first-switch.md | 83 +++++++++++++++++++ docs/plans/PROGRESS.md | 41 +++++++++ 2 files changed, 124 insertions(+) create mode 100644 docs/plans/2026-10-21-K95.1-seamless-first-switch.md diff --git a/docs/plans/2026-10-21-K95.1-seamless-first-switch.md b/docs/plans/2026-10-21-K95.1-seamless-first-switch.md new file mode 100644 index 0000000..9f0d146 --- /dev/null +++ b/docs/plans/2026-10-21-K95.1-seamless-first-switch.md @@ -0,0 +1,83 @@ +# K95.1 首次切换频道无感显示 + +## 目标 + +- 推荐首屏成功后后台预取 anime/live,避免第一次切换出现阻断式 loading。 +- 目标频道尚未返回时保留上一频道 PlayerPage 和视频画面,只叠加轻量 loading 遮罩。 +- 数据到达后原子替换为新频道 feed,并从第 1 个播放。 + +## 分支 + +- ReelShort:`fix/k95.1-seamless-first-switch`,从已包含 K95 的 `origin/develop` 创建。 +- FruitServer 仅文档留痕,不改功能代码。 + +## A. HomeFeedController + +新增 `Future prefetchChannels()`: + +- 仅处理 anime/live。 +- 只对 `!loaded && !loading` 的频道请求。 +- 请求结果写回对应频道缓存。 +- 不切换当前频道、不改变当前列表;仅目标恰为当前频道时 notify。 +- 失败只写 lastError,不向外抛。 +- 成功预取设置一次性 `skipNextRefresh`:首次切到该频道直接使用缓存,不立刻重复静默刷新; + 后续再次切回仍执行正常缓存静默刷新。 +- 预取期间用户切换到该频道时等待已有 inFlight,不重复请求。 + +## B. HomePage / _HomeTabs + +### B1 最近可见 feed + +`_HomeTabsState` 维护: + +- `List? _visibleDramas` +- `HomeFeedChannel? _visibleChannel` + +当前频道有数据时更新这两个值,并以当前频道渲染 PlayerPage。 + +### B2 首次切换 + +当前频道无数据且 loading: + +- 继续复用上一频道 `_visibleDramas/_visibleChannel` 渲染同一个 PlayerPage。 +- header 使用当前选中频道,但 PlayerPage.feedChannel 仍使用可见旧频道,避免队列更新。 +- Stack 上叠加 `IgnorePointer + ColoredBox(black26) + 24px spinner`, + Key 为 `home-feed-channel-loading`。 +- 数据返回后页面树保持同一层级的 PlayerPage,传入新 feed/channel,触发原子更新和第 1 个起播。 +- 空结果/失败后才切到「暂无剧目 / 重试」占位。 + +### B3 Prefetch 触发 + +推荐首屏后台刷新成功后 `unawaited(prefetchChannels())`。 + +## C. 测试 + +HomeFeedController: + +- prefetch 后首次 select anime 不再发请求。 +- prefetch 期间选择 anime 不重复请求。 +- prefetch 失败不影响推荐,也不向外抛。 + +Widget: + +- 首切未缓存频道时 PlayerPage 仍在树中且出现 loading 遮罩。 +- 数据返回后遮罩消失并使用新频道 feed。 + +门禁: + +- `dart analyze lib test` +- `flutter test --no-pub` +- `flutter build apk --debug` + +## 风险与控制 + +1. 预取完成与 select 同时发生时,select 等待同一 inFlight,避免重复请求。 +2. 加载遮罩 IgnorePointer,不阻断顶栏/底栏交互。 +3. PlayerPage 在正常和 loading 状态都放在相同 Stack 层级,保证 State 不因分支切换被销毁。 +4. 不改后端、ReelShortWeb、specs;不安装或操作真机。 + +## Git + +- 提交:`fix(home): 首切频道预取并保留播放画面` +- `--no-ff` 合回 ReelShort `develop` 并 push。 +- 作者:`Zhou Weidong <502333560@qq.com>`;禁止 `git add -A`。 diff --git a/docs/plans/PROGRESS.md b/docs/plans/PROGRESS.md index a385fcf..543ee51 100644 --- a/docs/plans/PROGRESS.md +++ b/docs/plans/PROGRESS.md @@ -24113,6 +24113,7 @@ Running Gradle task 'assembleDebug'... 24.8s - 门禁:`dart analyze lib test` 零告警;`flutter test --no-pub` 539 项全过; `flutter build apk --debug` 成功生成 `build/app/outputs/flutter-apk/app-debug.apk`。 - 未改后端、ReelShortWeb、specs;未安装或操作真机,等待用户验收。 + - 已完成数据层:新增 `FeedPage`,`CatalogApi.feed` 与 `CatalogRepository.fetchFeed` 支持 types/cursor/size,并补齐所有测试仓库实现。 - 已完成 `HomeFeedChannel` 与 `HomeFeedController`:推荐 seed、三频道独立缓存、 @@ -24163,3 +24164,43 @@ Running Gradle task 'assembleDebug'... 27.9s - 已先写计划 `docs/plans/2026-10-21-K95-feed-switch-loading.md`,明确频道请求归属、 loading/in-flight 状态、超时、Player feedRevision 与串行化方案。 - 不改后端、ReelShortWeb、specs;不安装或操作真机。 + +## 2026-10-21 K95.1 首次切换频道无感显示 + +- 已完整读取 K95.1 `docs/plans/INBOX.md`;本批次只改 ReelShort Flutter 客户端。 +- 已从包含 K95 的最新 ReelShort `origin/develop` 创建 + `fix/k95.1-seamless-first-switch` worktree。 +- 已先写计划 `docs/plans/2026-10-21-K95.1-seamless-first-switch.md`,明确后台预取、 + 最近可见 feed、非阻断遮罩和原子切换策略。 +- 已完成 `HomeFeedController.prefetchChannels()`:并行静默预取 anime/live 第一页, + 只处理未加载且无在飞请求的频道;预取失败不抛异常、不影响推荐。 +- 推荐首屏刷新成功后后台触发预取;预取成功首次切频道直接命中缓存, + 预取期间切换会等待同一请求,不重复发请求。 +- `_HomeTabsState` 保存最近一次非空 feed;目标频道首次加载时继续渲染上一个 + `PlayerPage`,叠加 `IgnorePointer` 半透明遮罩和 24px 转圈 + `home-feed-channel-loading`,数据到达后切到新频道第 1 个,空结果/失败显示空态。 +- 已补控制器预取、失败隔离、不重复请求、首切保留播放器及空结果测试。 + +门禁原始输出: + +```text +$ dart analyze lib test +Analyzing lib, test... +No issues found! + +$ flutter test --no-pub +00:54 +544: All tests passed! + +$ flutter build apk --debug +Running Gradle task 'assembleDebug'... 8.7s +✓ Built build/app/outputs/flutter-apk/app-debug.apk +``` + +解读:静态分析零告警,544 项全量测试通过,debug APK 构建成功。 + +### K95.1 交付结果 + +- ReelShort 功能提交:`677e49f fix(home): 首切频道预取并保留播放画面`。 +- ReelShort develop 合并提交:`838ca2c merge: 合并 K95.1 首次切换频道无感`, + 已推送 `dfa79be..838ca2c`;K95 与 K95.1 已统一合回 `develop`。 +- 未改后端、ReelShortWeb、specs;未安装或操作真机,等待用户验收。