001project_wildgrowth/ios/WildGrowth/COMPLETION_NAVIGATION_审查报告.md

134 lines
7.6 KiB
Markdown
Raw 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.

# 完成页导航方案 — 审查报告
**审查对象**:基于「零副作用 / SSOT / 响应式导航 / 原生手感」的完成页与播放器导航改造方案
**审查结论**:只做审查,不修改代码;本报告供决策与后续实现参考。
---
## 一、方案目标与标准(引用)
| 标准 | 含义 |
|-----|------|
| **零副作用** | 不引入「假页面」污染数据源,无 Hack |
| **单一事实来源 (SSOT)** | 数据源 `allCourseNodes` 仅含真实课程章节 |
| **响应式导航** | 用 `NavigationPath` 堆栈精确控制流向,消除白屏回退 |
| **原生手感** | 用手势识别(非页面索引)触发跳转 |
---
## 二、方案要点摘要
1. **删除 CompletionPlaceholderPage 的导航职责**(或整页删除,视实现而定)
- 若保留:仅作纯视觉占位,无 `onAppear` 等导航逻辑。
2. **VerticalScreenPlayerView**
- 在**最后一节**挂载「边缘左滑手势」修饰符(如 `LastPageSwipeModifier`)。
- 左滑触发:`navigationPath.append(CourseNavigation.completion(...))`,或先设 `currentNodeId = "wg://completion"` 再由现有 `onChange` 统一 push。
- TabView 仅渲染真实 `allCourseNodes`,不把「完成」当作一节数据。
3. **CompletionView**
- 新增 `@Binding var navigationPath: NavigationPath`
- 「回到我的内容」/「继续学习」调用 `handleReturnToMap()``navigationPath.removeLast(2)`,一次 pop 完成页 + 播放器,直接回地图。
4. **调用方**
- GrowthView / ProfileView / DiscoveryView 的 `.completion` 分支向 `CompletionView` 传入对应 path 的 `Binding`(如 `$navStore.growthPath` 等)。
---
## 三、与当前实现的对照
| 维度 | 当前实现 | 方案 |
|------|----------|------|
| **数据源** | `allCourseNodes` 纯净TabView 多一页 `CompletionPlaceholderPage` 用 tag `"wg://completion"`**未**写入 `allCourseNodes` | 与当前一致或更进一步:完全移除占位页,仅手势触发 push |
| **进入完成页** | 左滑到占位页 → `onChange(of: currentNodeId)` → 0.1s 后 append `.completion` | 最后一节左滑手势 → 直接 append 或先切 tag 再 append不依赖「多一页」 |
| **完成页返回** | `navStore.switchToGrowthTab()` + `dismiss()`,先回播放器再靠系统/逻辑 | `removeLast(2)` 穿透回地图,无中间层 |
| **占位页** | 存在,纯视觉 + 父视图 `onChange` 驱动 push无占位内 `onAppear` | 方案 A保留为纯视觉方案 B删除仅手势 |
**结论**:方案在「单一事实来源」和「响应式导航」上比当前更彻底;当前已避免在数据源里掺假节点,但仍依赖 TabView 多一页占位。
---
## 四、按标准的符合度
### 4.1 零副作用
- **方案**:若不保留占位页,则无「假页」;若保留占位页且仅视觉、无逻辑,则也算零逻辑副作用。
- **当前**:占位页无 `onAppear` 导航,副作用已收敛,但 TabView 仍多一个「虚拟页」。
- **符合度**:方案完全符合;当前基本符合,方案更干净。
### 4.2 单一事实来源 (SSOT)
- **方案**`allCourseNodes` 仅课程章节;完成页由导航栈 + 手势驱动,不进入数据模型。
- **当前**`allCourseNodes` 已是纯净的 `flatMap` 章节节点,未追加 placeholder node。
- **符合度**:方案与当前都符合;方案在视图层也不再依赖「多一页」的 tagSSOT 更纯粹。
### 4.3 响应式导航
- **方案**`CompletionView` 通过 `Binding<NavigationPath>` 执行 `removeLast(2)`,栈由 path 唯一决定,无 `switchToGrowthTab()` + `dismiss()` 的二次操作。
- **当前**:依赖 `dismiss()` 回播放器,再从播放器回地图,存在中间层与潜在白屏/闪烁。
- **符合度**:方案明显更符合;当前有改进空间。
### 4.4 原生手感
- **方案**:最后一节用 `DragGesture`(或类似)识别左滑,不依赖「滑到下一 tab 索引」才触发。
- **当前**:依赖用户滑到「占位页」才触发,仍与 TabView 索引/选页绑定。
- **符合度**:方案更贴近「手势驱动」;当前是「页面索引 + 手势」混合。
---
## 五、与既有文档的一致性
### 5.1 COMPLETION_VIEW_UI_LAYER_SPEC.md
- 说明要求:返回用 `dismiss()`**不**接收或操作 `NavigationPath`
- **方案**:改为接收 `Binding<NavigationPath>``removeLast(2)`,与当前 spec 冲突。
- **建议**:若采用方案,需**同步更新** COMPLETION_VIEW_UI_LAYER_SPEC
- 写明「穿透式返回」为可选/推荐实现;
- 接口增加 `@Binding var navigationPath: NavigationPath`
- 底部按钮行为改为「可调用 `removeLast(2)` 直接回地图」,并注明调用方需传入对应 path 的 Binding。
### 5.2 COMPLETION_PAGE_MEANING.md
- 当前描述为:最后一节后再左滑一页进入占位页,占位页触发 push。
- **方案**:最后一节左滑即触发(或先切 tag 再触发),可完全去掉「多一页」概念。
- **建议**:若采用方案,更新 COMPLETION_PAGE_MEANING
- 「最后一页」仍指课程的最后一节;
- 进入完成页的方式改为「在最后一节左滑(手势)触发」,并注明是否保留占位页。
---
## 六、风险与注意点
1. **多入口 path**
CompletionView 在 Growth / Profile / Discovery 三处被 present需分别传入 `growthPath` / `profilePath` / `homePath` 的 Binding漏传或传错会导致 `removeLast(2)` 作用在错误栈上。建议:
- 在审查/实现时逐处确认传参;
- 或为 CompletionView 封装「当前栈」来源(例如 Environment 注入当前 path避免调用方误绑。
2. **NavigationPath.count**
`removeLast(2)` 前需保证 `path.count >= 2`(完成页 + 播放器)。若从深链或异常入口进入完成页,栈深度可能不足,需保留兜底(如 `dismiss()`)。
3. **手势与 TabView 滑动冲突**
最后一节左滑既会触发自定义手势,也是 TabView 的翻页手势。需通过 `minimumDistance`、`coordinateSpace` 或「边缘优先」等策略区分,避免误触或重复触发。建议在真机多测:最后一节左滑、快速连续左滑、斜滑。
4. **从完成页返回后的 Tab 状态**
当前实现:从完成页 dismiss 回播放器后,`onAppear` 里若 `currentNodeId == "wg://completion"` 会切回 `allCourseNodes.last?.id`。采用方案后,若使用 `removeLast(2)`,不会回到播放器,故无需再依赖该逻辑;若仍保留「先 dismiss 再回地图」的入口,需保留或等价处理该状态恢复。
---
## 七、审查结论与建议
| 项目 | 结论 |
|------|------|
| **架构与标准** | 方案满足「零副作用、SSOT、响应式导航、原生手感」四项标准且比当前实现更彻底。 |
| **与现有 spec** | 与 COMPLETION_VIEW_UI_LAYER_SPEC 的「不操作 NavigationPath」冲突需更新 spec。 |
| **实现成本** | 中等CompletionView 接口与三处调用方改动VerticalScreenPlayerView 增加手势与可选移除占位页;需回归测试返回与多入口。 |
| **建议** | 若采纳方案:
1. 先更新 COMPLETION_VIEW_UI_LAYER_SPEC 与 COMPLETION_PAGE_MEANING再改代码
2. CompletionView 保留 `path.count >= 2` 判断与 `dismiss()` 兜底;
3. 手势与 TabView 的冲突在真机验证,必要时加防抖或边缘区域限制;
4. 三处 navigationDestination 的 `Binding` 传参做清单检查,避免漏传/错传。 |
---
**报告日期**:基于当前代码与所提供方案整理,未对仓库做任何代码修改。