Files
workouts/Workouts/Views/Settings/SettingsView.swift
T
rzen 1a0c484177 Refresh CLAUDE.md for 2.0; link changelog from the version in About
- Rewrite the stale CoreData/CloudKit architecture docs to describe the
  2.0 iCloud Drive document architecture (JSON source of truth + SwiftData
  cache, SyncEngine/ICloudFileManager/ICloudFileMonitor, ULID document/
  entity/mapper split, AppServices DI, WatchConnectivity bridge, XcodeGen/
  Swift 6/iOS 26, real directory layout).
- Add an "Authoring the Changelog" section documenting the end-user,
  one-paragraph-per-entry, derive-but-rewrite-from-git-log convention.
- About screen: make the version line open the changelog (IndieAbout
  0.2.0 changelogDocument) and drop the separate "Changelog" link; bump
  the IndieAbout dependency to from: 0.2.0.

Claude-Session: https://claude.ai/code/session_01A9CfUa4E9Zd5swfoNsYPs7
2026-06-20 15:33:09 -04:00

127 lines
4.9 KiB
Swift

//
// SettingsView.swift
// Workouts
//
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//
import SwiftUI
import SwiftData
import IndieAbout
struct SettingsView: View {
@Environment(SyncEngine.self) private var sync
@Environment(\.modelContext) private var modelContext
@Environment(AppServices.self) private var services
@Query(sort: \Split.order) private var splits: [Split]
@AppStorage("restSeconds") private var restSeconds: Int = 45
@AppStorage("doneCountdownSeconds") private var doneCountdownSeconds: Int = 5
@State private var showingAddSplitSheet = false
var body: some View {
NavigationStack {
Form {
// MARK: - Workout Section
Section {
Stepper(value: $restSeconds, in: 10...180, step: 5) {
HStack {
Text("Rest Between Sets")
Spacer()
Text("\(restSeconds)s").foregroundColor(.secondary)
}
}
Stepper(value: $doneCountdownSeconds, in: 3...20, step: 1) {
HStack {
Text("Auto-Finish Countdown")
Spacer()
Text("\(doneCountdownSeconds)s").foregroundColor(.secondary)
}
}
} header: {
Text("Workout")
} footer: {
Text("How long the watch waits on the finish screen before completing an exercise automatically.")
}
// MARK: - Splits Section
Section(header: Text("Splits")) {
if splits.isEmpty {
HStack {
Spacer()
VStack(spacing: 8) {
Image(systemName: "dumbbell.fill")
.font(.largeTitle)
.foregroundColor(.secondary)
Text("No Splits Yet")
.font(.headline)
.foregroundColor(.secondary)
Text("Create a split to organize your workout routine.")
.font(.caption)
.foregroundColor(.secondary)
.multilineTextAlignment(.center)
}
.padding(.vertical)
Spacer()
}
} else {
ForEach(splits) { split in
NavigationLink {
SplitDetailView(split: split)
} label: {
HStack {
Image(systemName: split.systemImage)
.foregroundColor(Color.color(from: split.color))
.frame(width: 24)
Text(split.name)
Spacer()
Text("\(split.exercisesArray.count)")
.foregroundColor(.secondary)
}
}
}
}
Button {
showingAddSplitSheet = true
} label: {
HStack {
Image(systemName: "plus.circle.fill")
.foregroundColor(.accentColor)
Text("Add Split")
}
}
Button {
Task { await SplitSeeder.seedDefaults(into: modelContext, using: sync) }
} label: {
HStack {
Image(systemName: "wand.and.sparkles")
.foregroundColor(.accentColor)
Text("Add Starter Splits")
}
}
}
// MARK: - About Section
Section {
IndieAbout(configuration: AppInfoConfiguration(
documents: [
.license(extension: "md")
],
changelogDocument: .changelog()
))
}
}
.navigationTitle("Settings")
.sheet(isPresented: $showingAddSplitSheet) {
SplitAddEditView(split: nil)
}
.onChange(of: restSeconds) { _, _ in services.watchBridge.pushAll() }
.onChange(of: doneCountdownSeconds) { _, _ in services.watchBridge.pushAll() }
}
}
}