add minimal workout interface, not linked with exercises yet

This commit is contained in:
Felix Förtsch
2024-08-16 16:52:52 +02:00
parent e33f9e80bc
commit 97d4038fb6
7 changed files with 205 additions and 16 deletions

View File

@@ -13,10 +13,15 @@ struct ContentView: View {
var body: some View {
TabView {
WorkoutLibraryView()
.tabItem {
Image(systemName: "gauge.with.needle.fill")
Text("Workouts")
}
ExerciseLibraryView()
.tabItem {
Image(systemName: "figure.run.square.stack")
Text("Library")
Image(systemName: "figure.run.square.stack.fill")
Text("Exercises")
}
Text("Settings")
.tabItem {
@@ -29,5 +34,5 @@ struct ContentView: View {
#Preview {
ContentView()
.modelContainer(for: Exercise.self, inMemory: true)
.modelContainer(for: [Exercise.self, Workout.self], inMemory: true)
}

View File

@@ -11,17 +11,17 @@ struct ExerciseDetailsView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var modelContext
var item: Exercise?
var exercise: Exercise?
var isPresentedAsSheet: Bool = false
var body: some View {
Form {
TextField("Exercise Name", text: Binding(
get: { item?.name ?? "" },
get: { exercise?.name ?? "" },
set: { newName in
if item != nil {
item?.name = newName
if exercise != nil {
exercise?.name = newName
}
}
))
@@ -48,13 +48,12 @@ struct ExerciseDetailsView: View {
do {
try modelContext.save()
} catch {
print("Failed to save item: \(error.localizedDescription)")
print("Failed to save exercise: \(error.localizedDescription)")
}
}
}
}
#Preview {
let sampleItem = Exercise(name: "Sample Item")
ExerciseDetailsView(item: sampleItem)
ExerciseDetailsView(exercise: Exercise(name: "New Exercises Preview"))
}

View File

@@ -10,7 +10,7 @@ import SwiftData
struct ExerciseLibraryView: View {
@Environment(\.modelContext) private var modelContext
@Query private var items: [Exercise]
@Query private var exercises: [Exercise]
@State private var isPresentingNewItemSheet = false
@@ -25,15 +25,15 @@ struct ExerciseLibraryView: View {
var body: some View {
NavigationView {
List {
ForEach(items.sorted(by: { $0.name < $1.name })) { item in
NavigationLink(destination: ExerciseDetailsView(item: item)) {
ForEach(exercises.sorted(by: { $0.name < $1.name })) { item in
NavigationLink(destination: ExerciseDetailsView(exercise: item)) {
Text(item.name)
}
}
.onDelete(perform: deleteExercise)
}
.onAppear {
if items.isEmpty {
if exercises.isEmpty {
loadInitialData(exercises: initialDataSet)
}
}
@@ -49,7 +49,7 @@ struct ExerciseLibraryView: View {
.sheet(isPresented: $isPresentingNewItemSheet) {
let newItem = Exercise(name: "")
NavigationView {
ExerciseDetailsView(item: newItem, isPresentedAsSheet: true)
ExerciseDetailsView(exercise: newItem, isPresentedAsSheet: true)
.onDisappear {
if !newItem.name.isEmpty {
saveExercise(item: newItem)
@@ -72,7 +72,7 @@ struct ExerciseLibraryView: View {
private func deleteExercise(offsets: IndexSet) {
withAnimation {
for index in offsets {
modelContext.delete(items[index])
modelContext.delete(exercises[index])
}
try? modelContext.save()
}

View File

@@ -0,0 +1,26 @@
//
// Item.swift
// WorkoutsPlus
//
// Created by Felix Förtsch on 10.08.24.
//
import Foundation
import SwiftData
@Model
final class Workout {
var name: String
var timestamp: Date
private var exercises: [Exercise] = []
init(name: String = "", timestamp: Date = Date.now) {
self.name = name
self.timestamp = timestamp
}
func addExercise(_ exercise: Exercise) {
exercises.append(exercise)
}
}

View File

@@ -0,0 +1,60 @@
//
// ExerciseDetailsView.swift
// WorkoutsPlus
//
// Created by Felix Förtsch on 10.08.24.
//
import SwiftUI
struct WorkoutDetailsView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var modelContext
var workout: Workout?
var isPresentedAsSheet: Bool = false
var body: some View {
Form {
TextField("Workout Name", text: Binding(
get: { workout?.name ?? "" },
set: { newName in
if workout != nil {
workout?.name = newName
}
}
))
.toolbar {
if (isPresentedAsSheet) {
ToolbarItem(placement: .topBarLeading) {
Button("Cancel") {
dismiss()
}
}
}
ToolbarItem(placement: .topBarTrailing) {
Button("Save") {
saveWorkout()
dismiss()
}
}
}
}
}
private func saveWorkout() {
if modelContext.hasChanges {
do {
try modelContext.save()
} catch {
print("Failed to save workout: \(error.localizedDescription)")
}
}
}
}
#Preview {
WorkoutDetailsView(workout: Workout(name: ""))
}

View File

@@ -0,0 +1,98 @@
//
// WorkoutLibraryView.swift
// WorkoutsPlus
//
// Created by Felix Förtsch on 10.08.24.
//
import SwiftUI
import SwiftData
struct WorkoutLibraryView: View {
@Environment(\.modelContext) private var modelContext
@Query private var workouts: [Workout]
@State private var isPresentingNewItemSheet = false
let initialDataSet = [
"RR",
"Minimalist"
]
var body: some View {
NavigationView {
List {
ForEach(workouts.sorted(by: { $0.name < $1.name })) { workout in
NavigationLink(destination: WorkoutDetailsView(workout: workout)) {
Text(workout.name)
}
}
.onDelete(perform: deleteWorkout)
}
.onAppear {
if workouts.isEmpty {
// TODO: This behaviour puts something into [workouts] whenever(!) it is empty. It's not bound to the first ever start of the app or anything. Check if that's the behaviour we want.
loadInitialData(workouts: initialDataSet)
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
createNewWorkout()
} label: {
Label("Add Workout", systemImage: "plus")
}
}
}
.sheet(isPresented: $isPresentingNewItemSheet) {
let newWorkout = Workout(name: "")
NavigationView {
WorkoutDetailsView(workout: newWorkout, isPresentedAsSheet: true)
.onDisappear {
if !newWorkout.name.isEmpty {
saveWorkout(workout: newWorkout)
}
}
}
}
}
}
private func createNewWorkout() {
isPresentingNewItemSheet = true
}
private func saveWorkout(workout: Workout) {
modelContext.insert(workout)
try? modelContext.save()
}
private func deleteWorkout(offsets: IndexSet) {
withAnimation {
for index in offsets {
modelContext.delete(workouts[index])
}
try? modelContext.save()
}
}
private func loadInitialData(workouts: [String]) {
var items: [Workout] = []
for exercise in workouts {
let item = Workout(name: exercise)
items.append(item)
}
for item in items {
modelContext.insert(item)
}
try? modelContext.save()
}
}
#Preview {
WorkoutLibraryView()
.modelContainer(for: Workout.self, inMemory: true)
}

View File

@@ -13,6 +13,7 @@ struct WorkoutsPlusApp: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Exercise.self,
Workout.self
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)