mirror of
https://github.com/pepaskopec-blip/Graduately.git
synced 2026-10-27 07:04:35 +00:00
166 lines
5 KiB
Swift
166 lines
5 KiB
Swift
import SwiftUI
|
|
|
|
@main
|
|
struct GraduatelyApp: App {
|
|
@StateObject private var vm = AppModel()
|
|
|
|
var body: some Scene {
|
|
WindowGroup {
|
|
MacRoot(vm: vm)
|
|
}
|
|
.defaultSize(width: 1280, height: 840)
|
|
.windowResizability(.contentMinSize)
|
|
.windowToolbarStyle(.unified)
|
|
.windowStyle(.automatic)
|
|
.commands {
|
|
CommandGroup(replacing: .newItem) {}
|
|
CommandGroup(after: .sidebar) {
|
|
Button(vm.tr("search_home")) {
|
|
vm.tab = .practice
|
|
vm.practicePath = []
|
|
}
|
|
.keyboardShortcut("1", modifiers: [.command])
|
|
Button(vm.tr("stats")) {
|
|
vm.tab = .progress
|
|
}
|
|
.keyboardShortcut("2", modifiers: [.command])
|
|
Button(vm.tr("search")) {
|
|
vm.tab = .search
|
|
}
|
|
.keyboardShortcut("k", modifiers: [.command])
|
|
Button(vm.tr("settings")) {
|
|
vm.tab = .settings
|
|
}
|
|
.keyboardShortcut("3", modifiers: [.command])
|
|
}
|
|
}
|
|
|
|
Settings {
|
|
SettingsScreen(vm: vm)
|
|
.formStyle(.grouped)
|
|
.frame(minWidth: 480, minHeight: 520)
|
|
.tint(vm.palette.tint)
|
|
.preferredColorScheme(vm.mode == .dark ? .dark : .light)
|
|
}
|
|
}
|
|
}
|
|
|
|
struct MacRoot: View {
|
|
@ObservedObject var vm: AppModel
|
|
|
|
var body: some View {
|
|
tabs
|
|
.tint(vm.palette.tint)
|
|
.preferredColorScheme(vm.mode == .dark ? .dark : .light)
|
|
.overlay(alignment: .top) {
|
|
if showsUpdateAccessory {
|
|
UpdateBanner(vm: vm)
|
|
.padding(16)
|
|
}
|
|
}
|
|
.onAppear { vm.checkUpdate(false) }
|
|
.frame(minWidth: 980, minHeight: 640)
|
|
}
|
|
|
|
private var showsUpdateAccessory: Bool {
|
|
vm.update.canInstall || vm.update.status == "downloading" || vm.update.status == "staged"
|
|
}
|
|
|
|
private var tabs: some View {
|
|
TabView(selection: $vm.tab) {
|
|
Tab(vm.tr("search_home"), systemImage: "square.grid.2x2.fill", value: AppTab.practice) {
|
|
NavigationStack(path: $vm.practicePath) {
|
|
PracticeHome(vm: vm)
|
|
.navigationDestination(for: Route.self) { route in
|
|
RouteDestination(vm: vm, route: route)
|
|
}
|
|
}
|
|
}
|
|
Tab(vm.tr("stats"), systemImage: "chart.bar.fill", value: AppTab.progress) {
|
|
NavigationStack {
|
|
StatsScreen(vm: vm)
|
|
}
|
|
}
|
|
Tab(vm.tr("settings"), systemImage: "gearshape.fill", value: AppTab.settings) {
|
|
NavigationStack {
|
|
SettingsScreen(vm: vm)
|
|
}
|
|
}
|
|
Tab(vm.tr("search"), systemImage: "magnifyingglass", value: AppTab.search, role: .search) {
|
|
NavigationStack {
|
|
SearchScreen(vm: vm)
|
|
}
|
|
}
|
|
}
|
|
.tabViewStyle(.sidebarAdaptable)
|
|
.toolbar {
|
|
MacWindowToolbar(vm: vm)
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct MacWindowToolbar: ToolbarContent {
|
|
@ObservedObject var vm: AppModel
|
|
|
|
var body: some ToolbarContent {
|
|
ToolbarItemGroup(placement: .primaryAction) {
|
|
Button {
|
|
vm.tab = .search
|
|
} label: {
|
|
Label(vm.tr("search"), systemImage: "magnifyingglass")
|
|
}
|
|
.controlSize(.large)
|
|
.help(vm.tr("search"))
|
|
|
|
Button {
|
|
vm.tab = .progress
|
|
} label: {
|
|
Label(vm.tr("stats"), systemImage: "chart.bar.fill")
|
|
}
|
|
.controlSize(.large)
|
|
.help(vm.tr("stats"))
|
|
}
|
|
|
|
ToolbarSpacer(.fixed, placement: .primaryAction)
|
|
|
|
ToolbarItem(placement: .primaryAction) {
|
|
Button {
|
|
vm.tab = .settings
|
|
} label: {
|
|
Label(vm.tr("settings"), systemImage: "gearshape")
|
|
}
|
|
.controlSize(.large)
|
|
.help(vm.tr("settings"))
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct UpdateBanner: View {
|
|
@ObservedObject var vm: AppModel
|
|
|
|
var body: some View {
|
|
HStack(spacing: 12) {
|
|
Image(systemName: "arrow.down.app.fill")
|
|
.font(.title3)
|
|
Text(bannerText)
|
|
.font(.body)
|
|
Spacer()
|
|
if vm.update.canInstall {
|
|
Button(vm.tr("update_install")) { vm.installUpdate() }
|
|
.buttonStyle(.borderedProminent)
|
|
.controlSize(.large)
|
|
}
|
|
}
|
|
.padding(.horizontal, 18)
|
|
.padding(.vertical, 14)
|
|
.softSurface(cornerRadius: 16)
|
|
.frame(maxWidth: 640)
|
|
}
|
|
|
|
private var bannerText: String {
|
|
if let arg = vm.update.messageArg {
|
|
return vm.fmt(vm.update.messageKey, arg)
|
|
}
|
|
return vm.tr(vm.update.messageKey)
|
|
}
|
|
}
|