Graduately/ios/Maturita/MaturitaApp.swift
pepaskopec-blip 6c7336c7bb Add a SwiftUI iOS app that plays the same maturita content.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-18 14:01:01 +02:00

154 lines
5.8 KiB
Swift
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.

import SwiftUI
@main
struct MaturitaApp: App {
@StateObject private var vm = AppModel()
var body: some Scene {
WindowGroup {
MaturitaRoot(vm: vm)
}
}
}
struct MaturitaRoot: View {
@ObservedObject var vm: AppModel
var body: some View {
let p = vm.palette
ZStack {
p.base.ignoresSafeArea()
VStack(spacing: 0) {
HStack {
Text("maturita.c")
.font(.system(size: 15, weight: .medium))
.foregroundStyle(p.text)
Spacer()
IconHit(action: { vm.searchOpen = true }) { SearchIcon(color: p.text) }
IconHit(action: { vm.go(.stats) }) { StatsIcon(color: p.text) }
IconHit(action: { vm.settingsOpen = true }) { SettingsIcon(color: p.text) }
}
.padding(.horizontal, 12)
.padding(.vertical, 6)
if vm.update.canInstall || vm.update.status == "downloading" || vm.update.status == "staged" {
HStack {
Text(bannerText)
.font(.system(size: 13))
.foregroundStyle(p.text)
Spacer()
if vm.update.canInstall {
PillButton(label: vm.tr("update_install"), palette: p) { vm.installUpdate() }
}
}
.padding(.horizontal, 12)
.padding(.vertical, 10)
.background(p.surface1, in: RoundedRectangle(cornerRadius: 14, style: .continuous))
.padding(.horizontal, 12)
}
routeView
.id(vm.tick)
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
if vm.searchOpen {
SearchOverlay(vm: vm).ignoresSafeArea(edges: .bottom)
}
if vm.settingsOpen {
SettingsSheet(vm: vm).ignoresSafeArea(edges: .bottom)
}
}
.preferredColorScheme(vm.mode == .dark ? .dark : .light)
.onAppear { vm.checkUpdate(false) }
}
private var bannerText: String {
if let arg = vm.update.messageArg {
return vm.fmt(vm.update.messageKey, arg)
}
return vm.tr(vm.update.messageKey)
}
@ViewBuilder
private var routeView: some View {
switch vm.route {
case .welcome: WelcomeScreen(vm: vm)
case .subjects: SubjectsScreen(vm: vm)
case .stats: StatsScreen(vm: vm)
case .roadmap: RoadmapScreen(vm: vm)
case .unitMap(let id): UnitMapScreen(vm: vm, unitId: id)
case .germanEx(let unit, let ex): GermanExercise(vm: vm, unitId: unit, ex: ex)
case .vocab(let id): VocabExercise(vm: vm, unitId: id)
case .netYears: NetYearsScreen(vm: vm)
case .netMap:
LessonMapScreen(
vm: vm,
title: vm.tr("net_year1"),
sub: vm.tr("net_sub"),
nodes: vm.content.netLessons.map { l in
let id = l.int("id")
return MapNode(
id: "n\(id)",
label: vm.tr(l.str("titleKey")),
locked: false,
done: vm.progress.netDone(id),
current: !vm.progress.netDone(id),
onClick: { vm.go(.netLesson(id)) }
)
}
)
case .netLesson(let id):
if let l = vm.content.netLesson(id) {
SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), sub: vm.tr(l.str("subKey")), slides: l.arr("slides")) {
vm.go(.netEx(id))
}
}
case .netEx(let id): NetQuizScreen(vm: vm, id: id)
case .hwMap:
LessonMapScreen(
vm: vm,
title: vm.tr("Technické vybavení"),
sub: vm.tr("hw_sub"),
nodes: vm.content.hw.map { l in
let id = l.int("id")
return MapNode(
id: "h\(id)",
label: vm.tr(l.str("titleKey")),
locked: false,
done: vm.progress.hwDone(id),
current: !vm.progress.hwDone(id),
onClick: { vm.go(.hwLesson(id)) }
)
}
)
case .hwLesson(let id):
if let l = vm.content.hwLesson(id) {
SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), sub: vm.tr(l.str("subKey")), slides: l.arr("slides")) {
vm.go(.hwEx(id))
}
}
case .hwEx(let id): HwQuizScreen(vm: vm, id: id)
case .czechMap: CzechMapScreen(vm: vm)
case .mluvnice:
LessonMapScreen(
vm: vm,
title: vm.tr("Mluvnice"),
sub: "Cvičení z mluvnice – styl maturita / přijímačky z ČJL",
nodes: vm.content.mluvnice.map { m in
let n = m.int("id")
return MapNode(
id: "m\(n)",
label: m.str("name"),
locked: false,
done: vm.progress.mluvDone(n),
current: !vm.progress.mluvDone(n),
onClick: { vm.go(.mluvEx(n)) }
)
}
)
case .mluvEx(let n): MluvExercise(vm: vm, n: n)
case .readingList: BookListScreen(vm: vm)
case .book(let id): BookScreen(vm: vm, id: id)
case .bookQuiz(let id): LitQuizScreen(vm: vm, id: id)
case .bookPlot(let id): PlotScreen(vm: vm, id: id)
}
}
}