Files
notes/Notes/Views/Settings/SettingsView.swift
T
rzen b2f16e8600 Add Projects: third tab, swipe-to-file, per-project note capture
Notes gain an optional project (schema v4, additive). Projects derive
from note values like tags — no separate entity. Tab naming
(Projects/Categories) is a Settings choice applied across the UI.

Claude-Session: https://claude.ai/code/session_014esDWi42URLEC6Cj17hGQ3
2026-07-16 18:24:11 -04:00

90 lines
3.5 KiB
Swift

import IndieAbout
import IndieBackup
import SwiftUI
struct SettingsView: View {
@Environment(SyncEngine.self) private var syncEngine
@Environment(ContextService.self) private var contextService
@EnvironmentObject private var backupController: BackupController
@AppStorage(ProjectsNaming.storageKey) private var namingRaw = ProjectsNaming.projects.rawValue
var body: some View {
NavigationStack {
Form {
Section("iCloud") {
HStack {
Text("Status")
Spacer()
switch syncEngine.iCloudStatus {
case .checking:
Text("Checking…").foregroundStyle(.secondary)
case .available:
Label("Connected", systemImage: "checkmark.icloud")
.foregroundStyle(.green)
case .unavailable:
Label("Unavailable", systemImage: "xmark.icloud")
.foregroundStyle(.red)
}
}
if let error = syncEngine.lastSyncError {
Text(error)
.font(.footnote)
.foregroundStyle(.red)
}
Button("Rebuild Local Cache") {
Task { await syncEngine.rebuildCache() }
}
.disabled(syncEngine.iCloudStatus != .available || syncEngine.isSyncing)
}
Section("Transcription") {
NavigationLink {
TranscriptionLanguagesView()
} label: {
Label("Languages", systemImage: "character.bubble")
}
}
Section("Organization") {
// Renames the Projects tab plus its list headers and swipe/assign UI.
Picker("Group Notes By", selection: $namingRaw) {
ForEach(ProjectsNaming.allCases) { naming in
Text(naming.plural).tag(naming.rawValue)
}
}
}
Section("Context") {
ContextSummaryRow(
context: contextService.current,
isRefreshing: contextService.isRefreshing
)
if contextService.authorizationDenied {
Text("Location access is off. Notes can still be taken, but they won't resurface by place. Enable location for Notes in Settings.")
.font(.footnote)
.foregroundStyle(.secondary)
}
Button("Refresh Context") {
Task { await contextService.refresh() }
}
.disabled(contextService.isRefreshing)
}
BackupsSectionView(controller: backupController)
Section {
IndieAbout(configuration: .init(
documents: [
.license(filename: "LICENSE", extension: "md"),
.custom(title: "Changelog", filename: "CHANGELOG", extension: "md")
]
))
}
}
.formStyle(.grouped)
.navigationTitle("Settings")
}
}
}