001project_wildgrowth/ios/WildGrowth/COMPLETION_最终交付_isFirstNode...

7.2 KiB
Raw Blame History

最终交付代码审查反馈isFirstNodeInChapter 完全回滚版)

审查对象您提供的「CompletionView 全量替换 + VerticalScreenPlayerView 仅替换 Struct」的最终交付代码并已恢复 isFirstNodeInChapter 为含 .sorted { $0.order < $1.order } 的写法。
结论:仅反馈,不应用、不修改仓库内任何文件。


参数说明(白话)

说法 指什么 作用
CompletionView 仍为 3 个参数 构造时仍是 (courseId, courseTitle, completedLessonCount) GrowthView / ProfileView / DiscoveryView 里已有 CompletionView(courseId: ..., courseTitle: ..., completedLessonCount: ...) 的调用不用改;替换成新 UI 后接口不变,不会报错。
VerticalScreenPlayerView init 6 参 构造时仍是 (courseId, nodeId, initialScrollIndex?, navigationPath?, isLastNode?, courseTitle?) MapView / GrowthView / DiscoveryView / ProfileView 等传入的 6 个参数(或只传前几项、后几项用默认值)都不用改;只改播放器内部实现,调用方零改动。

应用后仅实现以下三点、且无多余修改

若您做这两步:① 全量替换 CompletionView.swift;② 仅替换 VerticalScreenPlayerView.swift 里的 struct VerticalScreenPlayerView(保留同文件内 HeaderConfig、LessonPageView 等其余代码),则:

# 需求 是否由本交付代码实现 说明
1 播放器最后一个小节左滑进入完结页,从完结页右滑回到最后一个小节页 完结页作为 TabView 的最后一页内嵌在播放器内,左滑最后一节→完结页,右滑完结页→最后一节,无 push/pop。
2 完结页的 UI以及从接口/数据获取「共完成多少个小节」 新 UI粉紫勋章、点击点亮数量来自 UserManager.shared.studyStats.lessons(应用内统计,通常由学习进度接口或本地完成逻辑更新)。
3 底部一个按钮,点击回到技能 Tab我的课程列表 按钮「回到我的内容」仅调用 navStore.switchToGrowthTab(),切到技能 Tab。

其他没有任何多余修改

  • 只动 2 个文件CompletionView.swift(全量)、VerticalScreenPlayerView.swift(仅主 struct
  • 不改动CourseNavigation、MainTabView、MapView、ProfileView、DiscoveryView、GrowthView、NoteTreeView、NoteListView 等所有其他页面与类型;不增删导航枚举、不改 Tab 结构、不改地图/发现/个人/技能页逻辑。
  • 完结页不再通过「占位页 + onChange push .completion」出现而是作为播放器 TabView 最后一页展示,因此无需也不会去改各 Tab 的 navigationDestination(for: .completion) 的写法(它们保留不动,只是从播放器内不再 push .completion

结论:应用本交付代码后,行为严格限于上述 1、2、3 三点,无其他多余修改。


一、isFirstNodeInChapter与当前仓库 100% 一致

对比项 当前仓库实现 您提供的交付代码 结论
章节内节点 先找到包含 nodeId 的 chapter再在该章内取 validNodes 遍历每个 chaptervalidNodes 语义等价
排序 chapter.nodes.filter { $0.status != .locked }.sorted { $0.order < $1.order } chapter.nodes.filter { $0.status != .locked }.sorted { $0.order < $1.order } 完全一致
首节判定 validNodes.first?.id == nodeId validNodes.first?.id == nodeId(在含该 node 的章内) 完全一致

结论:章节判定逻辑已完全回滚到与当前仓库一致的写法(含 .sorted { $0.order < $1.order }),不会改变现有「按 order 的章节首节」展示行为。


二、loadMapData无全局排序与当前一致

  • 当前仓库allCourseNodes = data.chapters.flatMap { $0.nodes }.filter { $0.status != .locked },无 .sorted
  • 交付代码realNodes = data.chapters.flatMap { $0.nodes }.filter { $0.status != .locked },仅 UI 层 items = realNodes.map { .lesson($0) }append(.completion),无全局排序。

结论:数据顺序与当前一致,未引入按 order 的整课排序。


三、CompletionView仅 UI 更新,接口兼容

项目 说明
构造参数 仍为 (courseId, courseTitle, completedLessonCount)GrowthView/ProfileView/DiscoveryView 等处已有调用无需改
依赖 仍使用 @EnvironmentObject private var navStore: NavigationStore
按钮 「回到我的内容」仅调用 navStore.switchToGrowthTab(),未调用 dismiss()
说明 在统一分页方案下,完结页作为 TabView 最后一页内嵌展示,无 push 栈,不调用 dismiss() 是正确行为

结论:可全量替换 CompletionView.swift,仅 UI 从「翻牌 + 打字机」改为「粉紫勋章 + 点击点亮」,对外接口与行为符合预期。


四、VerticalScreenPlayerView仅替换 Struct其余保留

项目 结论
init 6 个构造参数完整保留courseId, nodeId, initialScrollIndex?, navigationPath?, isLastNode?, courseTitle?MapView/GrowthView 等调用方无需改
PlayerItem 枚举 .lesson(MapNode) / .completion 仅用于 UI 数据源,不参与完成数等业务逻辑
allItems realNodes + 末尾 .completion 构成,无全局排序
currentPositionProgress 仅用 lesson 项计算,排除完结页,进度条正确
完结页展示 顶部进度条在 currentNodeId == "COMPLETION_PAGE" 时隐藏,逻辑正确
LessonPageView courseTitle: self.courseTitle ?? mapData?.courseTitle 可减少标题闪烁
错误态 保留加载失败 + 重试;交付代码中错误文案为 .foregroundColor(.gray),当前为 .inkSecondary,属风格差异,可酌情统一
合并范围 仅替换 struct VerticalScreenPlayerView { ... } 及其内部的 enum PlayerItem必须保留同文件内 HeaderConfigDuolingoProgressBarCourseProgressNavBarLessonPageViewLessonSkeletonView 等所有其他类型,不得整文件覆盖

五、对其他页面的影响

  • CourseNavigationMainTabViewMapViewProfileViewDiscoveryViewGrowthView 等无需因本交付代码而改动。
  • 调用方仍按现有方式传入 6 参(含 navigationPath?isLastNodecourseTitle);完结页由 TabView 内嵌展示,不再依赖 push .completion 或占位页 onChange

六、审查结论汇总

项目 结论
isFirstNodeInChapter 已完全回滚为含 .sorted { $0.order < $1.order } 的写法,与当前仓库 100% 一致
loadMapData 无全局排序,与当前一致
CompletionView 仅 UI 更新,可全量替换
VerticalScreenPlayerView 仅替换主视图 Struct保留同文件其余代码逻辑与展示符合「逻辑回滚 + 统一分页」目标
其他页面 无需改动,零影响

未对仓库内任何文件进行修改。