Files
FruitServer/docs/plans/2026-10-21-K95-feed-switch-loading.md

101 lines
3.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# K95 修复首页频道快速切换卡 Loading
## 目标
- 频道请求完成后始终写回发起请求的频道缓存,不再因用户切走而丢失结果。
- 每频道明确区分 loaded / loading / in-flight / error,无请求时绝不显示首屏转圈。
- 首屏和刷新请求增加可注入的 10 秒超时兜底。
- PlayerLogic 保证连续 feed 更新串行应用,只让最后一次 feed/session 生效。
## 分支
- ReelShort:`fix/k95-feed-switch-loading`,从 `origin/develop` 创建。
- FruitServer 仅提交计划与 PROGRESS,不改功能代码。
## A. HomeFeedController
每个频道状态增加:
- `List<Drama> dramas`
- `String? cursor`
- `bool hasMore`
- `bool loaded`
- `bool loading`
- `bool loadingMore`
- `String? lastError`
- `int requestSeq`
- `Future<bool>? inFlight`
请求规则:
1. 响应成功/失败都写入发起请求的频道状态。
2. 只有频道仍为当前频道时才 `notifyListeners`。
3. `requestSeq` 只比较同一频道的新旧请求,不参与跨频道过滤。
4. 超时时间通过构造参数注入,默认 10 秒。
5. `loadingFirstPage = !loaded && loading`。
6. `selectChannel`:
- 有缓存:同步显示,未在飞时后台静默刷新
- 无缓存且正在飞:等待现有请求,不重复发
- 无缓存且有历史错误:自动重试
- 从未加载:发起首屏请求
7. `refreshCurrent` 同样复用 in-flight,防止并发重复刷新。
8. `loadMore` 失败保留 cursor/hasMore,允许下次继续重试。
## B. PlayerLogic / PlayerPage
### B1 Feed revision
- PlayerLogic 增加 `int feedRevision`。
- 每次 `updateFeed` 自增 revision,记录最新目标 feed。
- `_replaceSession` 与 `selectEpisode` 在 await 后校验 revision,过期流程直接返回。
- 连续 updateFeed 使用串行队列:当前应用结束后再应用最新待处理 feed。
### B2 空队列与频道切换
- 空 feed 不创建新 session,只清空 queue/visibleIndices 并停播。
- 空 → 非空必须恢复 route visible,并 select 第 0 个。
- 频道变化通过额外 reset 标志传入 `updateFeed`,即使 drama id 列表相同,
也重置到第 1 个并开始播放。
### B3 PlayerPage 更新判定
- `didUpdateWidget` 用 `feedDramas` 的 drama id 列表签名判断内容变化。
- 同一列表的新 List 实例不重复 updateFeed。
- 频道变化始终触发 updateFeed,并传 `resetToFirst=true`。
## C. 测试
### HomeFeedController
- A 挂起 → B 成功 → A 成功:A/B loaded 均为 true,切回 A 立即有数据。
- 同频道旧失败响应不得覆盖新成功响应。
- 无在飞请求时 `loadingFirstPage=false`。
- 永不返回的请求在注入超时后结束 loading、写 lastError、loaded=true。
- 失败频道再次 select 自动重试。
- loadMore 失败保留原有 hasMore/cursor。
### PlayerLogic
- 连续 5 次 updateFeed,最终 queue/session 对应最后一次列表。
- 空 → 非空会创建 session 并 select 第 0 个。
- 频道变化时相同 id 列表也回到第 1 个。
门禁:
- `dart analyze lib test`
- `flutter test --no-pub`
- `flutter build apk --debug`
## 风险与控制
1. 跨频道响应写回缓存必须与同频道 seq 校验同时存在;前者解决缓存丢失,后者解决同频道旧请求覆盖新结果。
2. Player session 替换以 revision 作为唯一有效性判断,避免旧 close/select 回调干扰新 session。
3. 空队列期间不创建 PlaybackSession;恢复非空时重新创建并起播。
4. 不改后端、ReelShortWeb、specs;不安装或操作真机。
## Git
- 提交:`fix(home): 修复频道快速切换卡 loading`
- `--no-ff` 合回 ReelShort `develop` 并 push。
- 作者:`Zhou Weidong <502333560@qq.com>`;禁止 `git add -A`。