krz/orgstar

A native macOS editor for org-mode files. editor org-mode swift

Sources/OrgstarMobile/SettingsScreen.swift

16086b4cf2caff5328774b2cd5ae3ffe1ab65ca4
orgstar/Sources/OrgstarMobile/SettingsScreen.swift history · blame · raw

64 lines · 3154 bytes

 1#if os(iOS)
 2import OrgApp
 3import OrgCore
 4import SwiftUI
 5import UIKit
 6
 7/// Where settings come from: a folder with the Mac's `config.toml` and `capture.toml`, kept in
 8/// sync by iCloud Drive or another app.
 9struct SettingsScreen: View {
10    let model: MobileModel
11    @State private var choosing = false
12
13    var body: some View {
14        Form {
15            Section {
16                LabeledContent("Folder", value: model.configFolder?.lastPathComponent ?? "None")
17                Button(model.configFolder == nil ? "Choose Folder…" : "Choose Another Folder…") { choosing = true }
18                if model.configFolder != nil {
19                    Button("Stop Using This Folder", role: .destructive) { model.setConfigFolder(nil) }
20                }
21                ForEach(model.configProblems, id: \.self) { Text($0).foregroundStyle(.red) }
22            } header: {
23                Text("Configuration")
24            } footer: {
25                Text("A folder with config.toml and capture.toml, such as a synced copy of ~/.config/orgstar from your Mac. Changes to them apply as they sync.")
26            }
27            Section("In use") {
28                let _ = model.settingsVersion
29                LabeledContent("TODO keywords", value: todoKeywords)
30                LabeledContent("Agenda", value: agenda)
31                LabeledContent("Reminders", value: OrgPreferences.reminders() ? "\(OrgPreferences.reminderLead()) min before" : "Off")
32                LabeledContent("Capture templates", value: CaptureTemplates.load().templates.map(\.key).joined(separator: " "))
33                LabeledContent("Theme", value: UserDefaults.standard.string(forKey: "themeFile").flatMap { $0.isEmpty ? nil : $0 } ?? "Default")
34                LabeledContent("Font", value: font)
35            }
36        }
37        .navigationTitle("Settings")
38        .fileImporter(isPresented: $choosing, allowedContentTypes: [.folder]) { result in
39            if case .success(let url) = result { model.setConfigFolder(url) }
40        }
41    }
42
43    /// The span and start day, worded as the Mac's Settings word the start.
44    private var agenda: String {
45        func days(_ count: Int) -> String { count == 1 ? "1 day" : "\(count) days" }
46        let offset = OrgPreferences.agendaStartOffset()
47        let start = offset == 0 ? "today" : offset < 0 ? "\(days(-offset)) before today" : "\(days(offset)) after today"
48        return "\(days(OrgPreferences.agendaSpan())), starting \(start)"
49    }
50
51    /// The theme's family, or the system's monospaced font when it's unset or not on the device.
52    private var font: String {
53        let spec = model.theme
54        let family = spec.font.isEmpty || UIFont.fontNames(forFamilyName: spec.font).isEmpty ? "System monospaced" : spec.font
55        let missing = !spec.font.isEmpty && family != spec.font ? " (\(spec.font) isn't installed)" : ""
56        return "\(family), \(spec.fontSize) pt\(missing)"
57    }
58
59    private var todoKeywords: String {
60        OrgPreferences.orgSettings().todoSequences.map { ($0.active.map(\.name) + ["|"] + $0.done.map(\.name)).joined(separator: " ") }
61            .joined(separator: "; ")
62    }
63}
64#endif