add a refresh action
This commit is contained in:
@@ -14,6 +14,7 @@ struct RootView: View {
|
||||
@State private var courses: [Course] = []
|
||||
@State private var currentCourse: Course?
|
||||
@State private var courseStatusText = "正在同步"
|
||||
@State private var isRefreshingCourses = false
|
||||
@State private var showCourseGuardToast = false
|
||||
@State private var managedResults: [UserResultSummary] = []
|
||||
@State private var managedDubShares: [UserDubShareSummary] = []
|
||||
@@ -208,11 +209,29 @@ struct RootView: View {
|
||||
.font(.system(size: 26, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
.padding(.horizontal, 16)
|
||||
Text("选择一门课程开始训练")
|
||||
.font(.system(size: 13))
|
||||
.foregroundColor(Theme.textMuted)
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.top, 4)
|
||||
HStack(spacing: 10) {
|
||||
Text("选择一门课程开始训练")
|
||||
.font(.system(size: 13))
|
||||
.foregroundColor(Theme.textMuted)
|
||||
Spacer()
|
||||
Button {
|
||||
Task { await loadCourses() }
|
||||
} label: {
|
||||
Text(isRefreshingCourses ? "刷新中" : "刷新课程")
|
||||
.font(.system(size: 13, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
.padding(.horizontal, 12)
|
||||
.frame(height: 32)
|
||||
.background(Theme.buttonBlue)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(isRefreshingCourses)
|
||||
.opacity(isRefreshingCourses ? 0.6 : 1)
|
||||
.padding(.trailing, 16)
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.top, 4)
|
||||
Text(courseStatusText)
|
||||
.font(.system(size: 13))
|
||||
.foregroundColor(Theme.textMuted)
|
||||
@@ -509,10 +528,50 @@ struct RootView: View {
|
||||
// MARK: - Load Courses
|
||||
|
||||
private func loadCourses() async {
|
||||
guard !isRefreshingCourses else { return }
|
||||
isRefreshingCourses = true
|
||||
defer { isRefreshingCourses = false }
|
||||
courseStatusText = "正在同步"
|
||||
do {
|
||||
courses = try await AuthAPI.courses(token: auth.token)
|
||||
courseStatusText = "\(courses.count) 个课程"
|
||||
var fetched = try await AuthAPI.courses(token: auth.token)
|
||||
var boundarySucceeded = 0
|
||||
var boundaryFailed = 0
|
||||
courses = fetched
|
||||
if !fetched.isEmpty {
|
||||
courseStatusText = "\(fetched.count) 个课程,正在刷新句界"
|
||||
let refreshed = await catalogVM.preloadBoundaries(
|
||||
for: fetched.map { $0.toVideoSummary() }
|
||||
)
|
||||
boundarySucceeded = refreshed.succeeded
|
||||
boundaryFailed = refreshed.failed
|
||||
let refreshedByHash = Dictionary(uniqueKeysWithValues: refreshed.videos.map { ($0.videoHash, $0) })
|
||||
fetched = fetched.map { course in
|
||||
guard let video = refreshedByHash[course.videoHash] else { return course }
|
||||
return Course(
|
||||
videoHash: course.videoHash,
|
||||
title: course.title,
|
||||
durationMs: video.durationMs,
|
||||
language: course.language,
|
||||
sentenceCount: video.sentenceCount,
|
||||
streamUrl: course.streamUrl,
|
||||
enrolled: course.enrolled
|
||||
)
|
||||
}
|
||||
if currentCourse != nil,
|
||||
let video = refreshed.videos.first(where: { $0.videoHash == currentCourse?.videoHash }) {
|
||||
currentCourse = fetched.first(where: { $0.videoHash == video.videoHash })
|
||||
}
|
||||
}
|
||||
courses = fetched
|
||||
if fetched.isEmpty {
|
||||
courseStatusText = "0 个课程"
|
||||
} else if boundaryFailed == 0 {
|
||||
courseStatusText = "\(fetched.count) 个课程 · 句界已刷新"
|
||||
} else if boundarySucceeded == 0 {
|
||||
courseStatusText = "\(fetched.count) 个课程 · 句界刷新失败"
|
||||
} else {
|
||||
courseStatusText = "\(fetched.count) 个课程 · 句界部分刷新(失败 \(boundaryFailed))"
|
||||
}
|
||||
} catch {
|
||||
courseStatusText = "课程加载失败:\(error.localizedDescription)"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user