You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
116 lines
4.3 KiB
116 lines
4.3 KiB
import SwiftUI
|
|
import UniformTypeIdentifiers
|
|
|
|
enum Tab: String, CaseIterable, Identifiable {
|
|
case overview = "Übersicht", history = "Verlauf", persons = "Personen", tasks = "Aufgaben"
|
|
var id: String { rawValue }
|
|
}
|
|
|
|
/// Zustand der Oberfläche (welcher Bereich, welcher Eintrag wird gerade bearbeitet).
|
|
@MainActor
|
|
final class UIState: ObservableObject {
|
|
@Published var tab: Tab = .overview
|
|
@Published var draft: Entry?
|
|
@Published var toast: String?
|
|
|
|
func newEntry(task: String = "", person: String = "", partner: String = "") {
|
|
draft = Entry(id: "", date: Day.today, task: task, person: person, partner: partner)
|
|
}
|
|
func show(_ message: String) {
|
|
toast = message
|
|
Task { try? await Task.sleep(for: .seconds(3)); if toast == message { toast = nil } }
|
|
}
|
|
}
|
|
|
|
@main
|
|
struct MawariApp: App {
|
|
@StateObject private var store = Store()
|
|
@StateObject private var ui = UIState()
|
|
@StateObject private var updates = UpdateController()
|
|
|
|
var body: some Scene {
|
|
Window("Mawari", id: "main") {
|
|
ContentView()
|
|
.environmentObject(store)
|
|
.environmentObject(ui)
|
|
.frame(minWidth: 960, minHeight: 600)
|
|
}
|
|
.defaultSize(width: 1280, height: 820)
|
|
.commands {
|
|
CommandGroup(after: .appInfo) {
|
|
Button("Nach Updates suchen …") { updates.checkForUpdates() }
|
|
.disabled(!updates.canCheck)
|
|
}
|
|
CommandGroup(replacing: .newItem) {
|
|
Button("Neuer Eintrag") { ui.newEntry() }.keyboardShortcut("n")
|
|
}
|
|
CommandGroup(replacing: .importExport) {
|
|
Button("Export für Numbers …") { Exporter.exportXlsx(store: store, ui: ui) }.keyboardShortcut("e")
|
|
Divider()
|
|
Button("Sicherung speichern …") { Exporter.saveBackup(store: store, ui: ui) }
|
|
Button("Sicherung / HTML-Daten laden …") { Exporter.importBackup(store: store, ui: ui) }
|
|
Button("Datenordner im Finder zeigen") { NSWorkspace.shared.activateFileViewerSelecting([store.folder]) }
|
|
}
|
|
CommandMenu("Ansicht") {
|
|
ForEach(Array(Tab.allCases.enumerated()), id: \.element) { i, tab in
|
|
Button(tab.rawValue) { ui.tab = tab }.keyboardShortcut(KeyEquivalent(Character("\(i + 1)")))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ContentView: View {
|
|
@EnvironmentObject var store: Store
|
|
@EnvironmentObject var ui: UIState
|
|
|
|
var body: some View {
|
|
Group {
|
|
switch ui.tab {
|
|
case .overview: OverviewView()
|
|
case .history: HistoryView()
|
|
case .persons: PersonsView()
|
|
case .tasks: TasksView()
|
|
}
|
|
}
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
|
.background(Color(nsColor: .textBackgroundColor))
|
|
.toolbar {
|
|
ToolbarItem(placement: .principal) {
|
|
Picker("Bereich", selection: $ui.tab) {
|
|
ForEach(Tab.allCases) { Text($0.rawValue).tag($0) }
|
|
}
|
|
.pickerStyle(.segmented)
|
|
.frame(width: 380)
|
|
}
|
|
ToolbarItemGroup(placement: .primaryAction) {
|
|
Button { Exporter.exportXlsx(store: store, ui: ui) } label: {
|
|
Label("Export für Numbers", systemImage: "tablecells")
|
|
}
|
|
.help("Aktuellen Stand als Tabelle für Numbers speichern (⌘E)")
|
|
Button { ui.newEntry() } label: { Label("Neuer Eintrag", systemImage: "plus") }
|
|
.help("Neuer Eintrag (⌘N)")
|
|
}
|
|
}
|
|
.sheet(item: $ui.draft) { draft in
|
|
EntrySheet(entry: draft)
|
|
}
|
|
.overlay(alignment: .bottom) {
|
|
if let t = ui.toast ?? store.lastError {
|
|
Text(t)
|
|
.font(.callout)
|
|
.padding(.horizontal, 14).padding(.vertical, 8)
|
|
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 8))
|
|
.padding(.bottom, 18)
|
|
.transition(.opacity)
|
|
}
|
|
}
|
|
.animation(.easeInOut(duration: 0.2), value: ui.toast)
|
|
.tint(Theme.tokiwa)
|
|
}
|
|
}
|
|
|
|
extension Entry {
|
|
var isNew: Bool { id.isEmpty }
|
|
}
|