mirror of
https://github.com/pepaskopec-blip/Graduately.git
synced 2026-10-27 07:04:35 +00:00
Add year-4 civics lessons on the economy, the world and philosophy.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
0ee2f8e960
commit
50a23c334b
27 changed files with 2317 additions and 32 deletions
|
|
@ -143,6 +143,11 @@ final class AppModel: ObservableObject {
|
|||
refresh()
|
||||
}
|
||||
|
||||
func markOn4(_ id: Int) {
|
||||
progress.markOn4(id)
|
||||
refresh()
|
||||
}
|
||||
|
||||
func markMluv(_ n: Int) {
|
||||
progress.markMluv(n)
|
||||
refresh()
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ final class Content {
|
|||
let on: [J]
|
||||
let on2: [J]
|
||||
let on3: [J]
|
||||
let on4: [J]
|
||||
let mluvnice: [J]
|
||||
let books: [J]
|
||||
let changelog: [J]
|
||||
|
|
@ -68,6 +69,7 @@ final class Content {
|
|||
on = raw.arr("on")
|
||||
on2 = raw.arr("on2")
|
||||
on3 = raw.arr("on3")
|
||||
on4 = raw.arr("on4")
|
||||
mluvnice = raw.arr("mluvnice")
|
||||
books = raw.arr("books")
|
||||
changelog = raw.arr("changelog")
|
||||
|
|
@ -79,6 +81,7 @@ final class Content {
|
|||
func onLesson(_ id: Int) -> J? { on.first { $0.int("id") == id } }
|
||||
func on2Lesson(_ id: Int) -> J? { on2.first { $0.int("id") == id } }
|
||||
func on3Lesson(_ id: Int) -> J? { on3.first { $0.int("id") == id } }
|
||||
func on4Lesson(_ id: Int) -> J? { on4.first { $0.int("id") == id } }
|
||||
func mluv(_ n: Int) -> J? { mluvnice.first { $0.int("id") == n } }
|
||||
func book(_ id: String) -> J? { books.first { $0.str("id") == id } }
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@ final class ProgressStore {
|
|||
func on3Done(_ id: Int) -> Bool { defaults.bool(forKey: "on3.\(id)") }
|
||||
func markOn3(_ id: Int) { defaults.set(true, forKey: "on3.\(id)") }
|
||||
|
||||
func on4Done(_ id: Int) -> Bool { defaults.bool(forKey: "on4.\(id)") }
|
||||
func markOn4(_ id: Int) { defaults.set(true, forKey: "on4.\(id)") }
|
||||
|
||||
func mluvDone(_ n: Int) -> Bool { defaults.bool(forKey: "mluv.\(n)") }
|
||||
func markMluv(_ n: Int) { defaults.set(true, forKey: "mluv.\(n)") }
|
||||
|
||||
|
|
@ -110,6 +113,14 @@ func summarize(_ content: Content, _ p: ProgressStore) -> ProgressSum {
|
|||
sum.doneUnits += 1
|
||||
}
|
||||
}
|
||||
for l in content.on4 {
|
||||
sum.totalEx += 1
|
||||
sum.openUnits += 1
|
||||
if p.on4Done(l.int("id")) {
|
||||
sum.doneEx += 1
|
||||
sum.doneUnits += 1
|
||||
}
|
||||
}
|
||||
sum.totalEx += content.mluvnice.count
|
||||
sum.openUnits += 1
|
||||
let md = content.mluvnice.filter { p.mluvDone($0.int("id")) }.count
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ enum Route: Hashable {
|
|||
case on3Map
|
||||
case on3Lesson(Int)
|
||||
case on3Ex(Int)
|
||||
case on4Map
|
||||
case on4Lesson(Int)
|
||||
case on4Ex(Int)
|
||||
case czechMap
|
||||
case mluvnice
|
||||
case mluvEx(Int)
|
||||
|
|
@ -68,6 +71,9 @@ enum Route: Hashable {
|
|||
case .on3Map: return "on3map"
|
||||
case .on3Lesson(let id): return "on3unit\(id)"
|
||||
case .on3Ex(let id): return "on3ex\(id)"
|
||||
case .on4Map: return "on4map"
|
||||
case .on4Lesson(let id): return "on4unit\(id)"
|
||||
case .on4Ex(let id): return "on4ex\(id)"
|
||||
case .czechMap: return "czechmap"
|
||||
case .mluvnice: return "mluvnice"
|
||||
case .mluvEx(let n): return "mluve\(n)"
|
||||
|
|
@ -136,6 +142,12 @@ enum Route: Hashable {
|
|||
return [.onYears, .on3Map, .on3Lesson(id)]
|
||||
case .on3Ex(let id):
|
||||
return [.onYears, .on3Map, .on3Lesson(id), .on3Ex(id)]
|
||||
case .on4Map:
|
||||
return [.onYears, .on4Map]
|
||||
case .on4Lesson(let id):
|
||||
return [.onYears, .on4Map, .on4Lesson(id)]
|
||||
case .on4Ex(let id):
|
||||
return [.onYears, .on4Map, .on4Lesson(id), .on4Ex(id)]
|
||||
case .czechMap:
|
||||
return [.czechMap]
|
||||
case .mluvnice:
|
||||
|
|
@ -168,6 +180,7 @@ func routeFromPage(_ page: String) -> Route? {
|
|||
case "onmap": return .onMap
|
||||
case "on2map": return .on2Map
|
||||
case "on3map": return .on3Map
|
||||
case "on4map": return .on4Map
|
||||
case "czechmap": return .czechMap
|
||||
case "mluvnice": return .mluvnice
|
||||
case "readinglist": return .readingList
|
||||
|
|
@ -211,6 +224,12 @@ func routeFromPage(_ page: String) -> Route? {
|
|||
if let m = page.wholeMatch(of: /hwex(\d+)/), let n = Int(m.1) {
|
||||
return .hwEx(n)
|
||||
}
|
||||
if let m = page.wholeMatch(of: /on4unit(\d+)/), let n = Int(m.1) {
|
||||
return .on4Lesson(n)
|
||||
}
|
||||
if let m = page.wholeMatch(of: /on4ex(\d+)/), let n = Int(m.1) {
|
||||
return .on4Ex(n)
|
||||
}
|
||||
if let m = page.wholeMatch(of: /on3unit(\d+)/), let n = Int(m.1) {
|
||||
return .on3Lesson(n)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -682,6 +682,25 @@ struct On3QuizScreen: View {
|
|||
}
|
||||
}
|
||||
|
||||
struct On4QuizScreen: View {
|
||||
@ObservedObject var vm: AppModel
|
||||
let id: Int
|
||||
|
||||
var body: some View {
|
||||
if let lesson = vm.content.on4Lesson(id) {
|
||||
let spec = J([
|
||||
"type": "choice",
|
||||
"title": vm.tr(lesson.str("exTitleKey")),
|
||||
"sub": lesson.str("quizHeadKey"),
|
||||
"questions": lesson.rawArray("quiz"),
|
||||
])
|
||||
DispatchExercise(vm: vm, spec: spec, titleFallback: vm.tr(lesson.str("exTitleKey"))) {
|
||||
vm.markOn4(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct HwQuizScreen: View {
|
||||
@ObservedObject var vm: AppModel
|
||||
let id: Int
|
||||
|
|
|
|||
|
|
@ -423,11 +423,12 @@ private func subjectSum(_ vm: AppModel, _ s: J) -> ProgressSum {
|
|||
case "hwyears", "hwmap":
|
||||
sum.totalEx = vm.content.hw.count
|
||||
sum.doneEx = vm.content.hw.filter { vm.progress.hwDone($0.int("id")) }.count
|
||||
case "onyears", "onmap", "on2map", "on3map":
|
||||
sum.totalEx = vm.content.on.count + vm.content.on2.count + vm.content.on3.count
|
||||
case "onyears", "onmap", "on2map", "on3map", "on4map":
|
||||
sum.totalEx = vm.content.on.count + vm.content.on2.count + vm.content.on3.count + vm.content.on4.count
|
||||
sum.doneEx = vm.content.on.filter { vm.progress.onDone($0.int("id")) }.count
|
||||
+ vm.content.on2.filter { vm.progress.on2Done($0.int("id")) }.count
|
||||
+ vm.content.on3.filter { vm.progress.on3Done($0.int("id")) }.count
|
||||
+ vm.content.on4.filter { vm.progress.on4Done($0.int("id")) }.count
|
||||
case "czechmap":
|
||||
sum.totalEx = vm.content.mluvnice.count
|
||||
sum.doneEx = vm.content.mluvnice.filter { vm.progress.mluvDone($0.int("id")) }.count
|
||||
|
|
@ -569,7 +570,7 @@ private func buildSearch(_ vm: AppModel) -> [Hit] {
|
|||
add(vm.tr("on_year1"), vm.tr("search_lesson"), "onmap", extra: "rocnik")
|
||||
add(vm.tr("on_year2"), vm.tr("search_lesson"), "on2map", extra: "rocnik")
|
||||
add(vm.tr("on_year3"), vm.tr("search_lesson"), "on3map", extra: "rocnik")
|
||||
add(vm.tr("on_year4"), vm.tr("search_lesson"), "onyears", locked: true, extra: "rocnik")
|
||||
add(vm.tr("on_year4"), vm.tr("search_lesson"), "on4map", extra: "rocnik")
|
||||
for l in vm.content.on {
|
||||
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "onunit\(l.int("id"))", extra: "obcanka")
|
||||
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "onex\(l.int("id"))")
|
||||
|
|
@ -582,6 +583,10 @@ private func buildSearch(_ vm: AppModel) -> [Hit] {
|
|||
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "on3unit\(l.int("id"))", extra: "obcanka")
|
||||
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "on3ex\(l.int("id"))")
|
||||
}
|
||||
for l in vm.content.on4 {
|
||||
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "on4unit\(l.int("id"))", extra: "obcanka")
|
||||
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "on4ex\(l.int("id"))")
|
||||
}
|
||||
add(vm.tr("Mluvnice"), vm.tr("search_lesson"), "mluvnice")
|
||||
add(vm.tr("Maturitní četba"), vm.tr("search_book"), "readinglist")
|
||||
for b in vm.content.books {
|
||||
|
|
@ -812,13 +817,8 @@ struct OnYearsScreen: View {
|
|||
NavigationLink(value: Route.on3Map) {
|
||||
ListRowLabel(title: vm.tr("on_year3"), subtitle: vm.tr("on3_sub"))
|
||||
}
|
||||
ForEach(["on_year4"], id: \.self) { key in
|
||||
Label {
|
||||
ListRowLabel(title: vm.tr(key), subtitle: vm.tr("on_year_locked_sub"))
|
||||
} icon: {
|
||||
Image(systemName: "lock.fill")
|
||||
}
|
||||
.foregroundStyle(.secondary)
|
||||
NavigationLink(value: Route.on4Map) {
|
||||
ListRowLabel(title: vm.tr("on_year4"), subtitle: vm.tr("on4_sub"))
|
||||
}
|
||||
}
|
||||
.navigationTitle(vm.tr("Občanská nauka"))
|
||||
|
|
@ -1460,6 +1460,27 @@ struct RouteDestination: View {
|
|||
}
|
||||
case .on3Ex(let id):
|
||||
On3QuizScreen(vm: vm, id: id)
|
||||
case .on4Map:
|
||||
LessonListScreen(
|
||||
vm: vm,
|
||||
title: vm.tr("on_year4"),
|
||||
subtitle: vm.tr("on4_sub"),
|
||||
rows: vm.content.on4.map { l in
|
||||
let id = l.int("id")
|
||||
return LessonRow(
|
||||
id: "o4\(id)",
|
||||
title: vm.tr(l.str("titleKey")),
|
||||
done: vm.progress.on4Done(id),
|
||||
destination: .on4Lesson(id)
|
||||
)
|
||||
}
|
||||
)
|
||||
case .on4Lesson(let id):
|
||||
if let l = vm.content.on4Lesson(id) {
|
||||
SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .on4Ex(id))
|
||||
}
|
||||
case .on4Ex(let id):
|
||||
On4QuizScreen(vm: vm, id: id)
|
||||
case .czechMap:
|
||||
CzechMapScreen(vm: vm)
|
||||
case .mluvnice:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue