diff --git a/Werkout_ios.xcodeproj/project.pbxproj b/Werkout_ios.xcodeproj/project.pbxproj index da03d13..cfbc046 100644 --- a/Werkout_ios.xcodeproj/project.pbxproj +++ b/Werkout_ios.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 1C485C832A489B9C00A6F896 /* CompletedWorkouts.json in Resources */ = {isa = PBXBuildFile; fileRef = 1C485C822A489B9C00A6F896 /* CompletedWorkouts.json */; }; + 1C485C872A4915C400A6F896 /* CreateWorkoutItemPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C485C862A4915C400A6F896 /* CreateWorkoutItemPickerView.swift */; }; 1CF65A262A3972840042FFBD /* Werkout_iosApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A252A3972840042FFBD /* Werkout_iosApp.swift */; }; 1CF65A282A3972840042FFBD /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A272A3972840042FFBD /* Persistence.swift */; }; 1CF65A2B2A3972840042FFBD /* Werkout_ios.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A292A3972840042FFBD /* Werkout_ios.xcdatamodeld */; }; @@ -95,6 +96,7 @@ /* Begin PBXFileReference section */ 1C485C822A489B9C00A6F896 /* CompletedWorkouts.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = CompletedWorkouts.json; sourceTree = ""; }; + 1C485C862A4915C400A6F896 /* CreateWorkoutItemPickerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateWorkoutItemPickerView.swift; sourceTree = ""; }; 1CF65A222A3972840042FFBD /* Werkout_ios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Werkout_ios.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1CF65A252A3972840042FFBD /* Werkout_iosApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Werkout_iosApp.swift; sourceTree = ""; }; 1CF65A272A3972840042FFBD /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; @@ -275,6 +277,7 @@ 1CF65A6A2A3C1EAC0042FFBD /* CreateWorkoutMainView.swift */, 1CF65A722A3F60D20042FFBD /* CreateExerciseActionsView.swift */, 1CF65A7A2A3F83440042FFBD /* CreateWorkoutSupersetActionsView.swift */, + 1C485C862A4915C400A6F896 /* CreateWorkoutItemPickerView.swift */, ); path = CreateWorkout; sourceTree = ""; @@ -487,6 +490,7 @@ 1CF65A5B2A3BF4BE0042FFBD /* Equipment.swift in Sources */, 1CF65A452A39FB550042FFBD /* Exercise.swift in Sources */, 1CF65A612A3BF6020042FFBD /* AddExerciseView.swift in Sources */, + 1C485C872A4915C400A6F896 /* CreateWorkoutItemPickerView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Werkout_ios/Views/AddExercise/AddExerciseView.swift b/Werkout_ios/Views/AddExercise/AddExerciseView.swift index fff8608..f926f0e 100644 --- a/Werkout_ios/Views/AddExercise/AddExerciseView.swift +++ b/Werkout_ios/Views/AddExercise/AddExerciseView.swift @@ -10,35 +10,37 @@ import SwiftUI import Combine struct AddExerciseView: View { + enum CreateWorkoutItemPickerViewType { + case muscles + case equipment + } + @State var selectedMuscles = [Muscle]() @State var selectedEquipment = [Equipment]() @State var filteredExercises = [ExerciseExercise]() - - @State var searchString: String = "" - + @StateObject var bridgeModule = BridgeModule.shared @Environment(\.dismiss) var dismiss var selectedWorkout: ((ExerciseExercise) -> Void) + @State var createWorkoutItemPickerViewModel: CreateWorkoutItemPickerViewModel? + @State var createWorkoutItemPickerViewType: CreateWorkoutItemPickerViewType? var body: some View { VStack { - muscleView() - - Divider() - - equipmentView() - - Divider() - - TextField("Filter", text: $searchString) - .onReceive(Just(searchString)) { location in - filterExercises() - } - .padding() - - Divider() - + HStack { + muscleView() + .frame(maxWidth: .infinity) + + Divider() + + equipmentView() + .frame(maxWidth: .infinity) + } + .padding(.top) + .frame(height: 44) + exerciseView() + .padding(.top) } .onAppear{ if #function.hasPrefix("__preview") { @@ -53,6 +55,38 @@ struct AddExerciseView: View { selectedEquipment = equipment filteredExercises = exercises } + .sheet(item: $createWorkoutItemPickerViewModel) { item in + CreateWorkoutItemPickerView(viewModel: item, completed: { selectedids in + if let viewType = createWorkoutItemPickerViewType { + switch viewType { + case .muscles: + if let muscles = DataStore.shared.allMuscles { + selectedMuscles.removeAll() + for id in selectedids { + if let muscle = muscles.first(where: { + $0.id == id + }) { + selectedMuscles.append(muscle) + } + } + } + case .equipment: + if let equipment = DataStore.shared.allEquipment { + selectedEquipment.removeAll() + for id in selectedids { + if let equipment = equipment.first(where: { + $0.id == id + }) { + selectedEquipment.append(equipment) + } + } + } + } + } + + filterExercises() + }) + } .background(Color(uiColor: .tertiarySystemBackground)) } @@ -97,94 +131,57 @@ struct AddExerciseView: View { } } - if !searchString.isEmpty { - return exercise.name.contains(searchString) && hasCorrectMuscles && hasCorrectEquipment - } else { - return hasCorrectMuscles && hasCorrectEquipment - } + return hasCorrectMuscles && hasCorrectEquipment }) } func muscleView() -> some View { VStack { + if let _ = DataStore.shared.allMuscles { + Text("Select Muscles") + .foregroundColor(.cyan) + Text("\(selectedMuscles.count) Selected") + } + } + .onTapGesture { if let muscles = DataStore.shared.allMuscles { - Button("toggle all", action: { - if self.selectedMuscles.count > 0 { - self.selectedMuscles.removeAll() - } else { - self.selectedMuscles.append(contentsOf: muscles) - } - filterExercises() + var createWorkoutItemPickerModels = [CreateWorkoutItemPickerModel]() + muscles.forEach({ + let model = CreateWorkoutItemPickerModel(id: $0.id, name: $0.name) + createWorkoutItemPickerModels.append(model) }) - ScrollView(.horizontal) { - HStack(spacing: 10) { - ForEach(muscles, id:\.id) { muscle in - ZStack { - RoundedRectangle(cornerRadius: 8) - .stroke(selectedMuscles.contains(where: { $0.id == muscle.id }) ? .green : .gray, lineWidth: 3) - .frame(maxWidth: .infinity, maxHeight: .infinity) - Text(muscle.name) - .lineLimit(2) - .multilineTextAlignment(.center) - .frame(maxWidth: .infinity, maxHeight: .infinity) - } - .contentShape(Rectangle()) - .onTapGesture { - if selectedMuscles.contains(where: { $0.id == muscle.id }) { - self.selectedMuscles.removeAll(where: { - $0.id == muscle.id - }) - } else { - self.selectedMuscles.append(muscle) - } - filterExercises() - } - .frame(width: 150, height: 70) - } - }.padding() - }.frame(height: 100) + createWorkoutItemPickerModels = createWorkoutItemPickerModels.sorted(by: { + $0.name < $1.name + }) + let selectedIds = selectedMuscles.map { $0.id } + createWorkoutItemPickerViewModel = CreateWorkoutItemPickerViewModel(allValues: createWorkoutItemPickerModels, selectedIds: selectedIds) + createWorkoutItemPickerViewType = .muscles } } } func equipmentView() -> some View { VStack { + if let _ = DataStore.shared.allEquipment { + Text("Select Equipment") + .foregroundColor(.cyan) + Text("\(selectedEquipment.count) Selected") + } + } + .onTapGesture { if let equipment = DataStore.shared.allEquipment { - Button("toggle all", action: { - if self.selectedEquipment.count > 0 { - self.selectedEquipment.removeAll() - } else { - self.selectedEquipment.append(contentsOf: equipment) - } - filterExercises() + var createWorkoutItemPickerModels = [CreateWorkoutItemPickerModel]() + equipment.forEach({ + let model = CreateWorkoutItemPickerModel(id: $0.id, + name: $0.name ?? "-") + createWorkoutItemPickerModels.append(model) }) - ScrollView(.horizontal) { - HStack(spacing: 10) { - ForEach(equipment, id:\.id) { aequipment in - ZStack { - RoundedRectangle(cornerRadius: 8) - .stroke(selectedEquipment.contains(where: { $0.id == aequipment.id }) ? .green : .gray, lineWidth: 3) - .frame(maxWidth: .infinity, maxHeight: .infinity) - Text(aequipment.name ?? "--") - .frame(maxWidth: .infinity, maxHeight: .infinity) - .lineLimit(2) - .multilineTextAlignment(.center) - } - .contentShape(Rectangle()) - .onTapGesture { - if selectedEquipment.contains(where: { $0.id == aequipment.id }) { - self.selectedEquipment.removeAll(where: { - $0.id == aequipment.id - }) - } else { - self.selectedEquipment.append(aequipment) - } - filterExercises() - } - .frame(width: 150, height: 70) - } - }.padding() - }.frame(height: 100) + createWorkoutItemPickerModels = createWorkoutItemPickerModels.sorted(by: { + $0.name < $1.name + }) + let selectedIds = selectedEquipment.map { $0.id } + createWorkoutItemPickerViewModel = CreateWorkoutItemPickerViewModel(allValues: createWorkoutItemPickerModels, selectedIds: selectedIds) + createWorkoutItemPickerViewType = .equipment } } } diff --git a/Werkout_ios/Views/CreateWorkout/CreateWorkoutItemPickerView.swift b/Werkout_ios/Views/CreateWorkout/CreateWorkoutItemPickerView.swift new file mode 100644 index 0000000..39cc66b --- /dev/null +++ b/Werkout_ios/Views/CreateWorkout/CreateWorkoutItemPickerView.swift @@ -0,0 +1,112 @@ +// +// CreateWorkoutItemPicker.swift +// Werkout_ios +// +// Created by Trey Tartt on 6/25/23. +// + +import SwiftUI +import Combine + +struct CreateWorkoutItemPickerModel { + let id: Int + let name: String +} + +class CreateWorkoutItemPickerViewModel: Identifiable, ObservableObject { + let allValues: [CreateWorkoutItemPickerModel] + @Published var selectedIds: [Int] + + init(allValues: [CreateWorkoutItemPickerModel], selectedIds: [Int]) { + self.allValues = allValues + self.selectedIds = selectedIds + } + + func toggleAll() { + if selectedIds.isEmpty { + selectedIds.append(contentsOf: allValues.map({ $0.id })) + } else { + selectedIds.removeAll() + } + } +} + +struct CreateWorkoutItemPickerView: View { + @ObservedObject var viewModel: CreateWorkoutItemPickerViewModel + var completed: (([Int]) -> Void) + @Environment(\.dismiss) var dismiss + @State var searchString: String = "" + + var body: some View { + VStack { + List() { + ForEach(viewModel.allValues, id:\.self.id) { value in + if searchString.isEmpty || value.name.lowercased().contains(searchString.lowercased()) { + HStack { + Circle() + .stroke(.blue, lineWidth: 1) + .background(Circle().fill(viewModel.selectedIds.contains(value.id) ? .blue :.clear)) + .frame(width: 33, height: 33) + + Text(value.name) + } + .contentShape(Rectangle()) + .onTapGesture { + if viewModel.selectedIds.contains(value.id) { + if let idx = viewModel.selectedIds.firstIndex(of: value.id){ + viewModel.selectedIds.remove(at: idx) + } + } else { + viewModel.selectedIds.append(value.id) + } + } + } + } + } + + TextField("Filter", text: $searchString) + .padding() + + HStack { + Button(action: { + viewModel.toggleAll() + }, label: { + Image(systemName: "checklist") + .font(.title) + }) + .frame(maxWidth: 44, alignment: .center) + .frame(height: 44) + .foregroundColor(.green) + .background(.white) + .cornerRadius(8) + .padding() + + Button(action: { + completed(viewModel.selectedIds) + dismiss() + }, label: { + Text("done") + }) + .frame(maxWidth: .infinity, alignment: .center) + .frame(height: 44) + .foregroundColor(.blue) + .background(.yellow) + .cornerRadius(8) + .padding() + .frame(maxWidth: .infinity) + } + } + } +} + +struct CreateWorkoutItemPickerView_Previews: PreviewProvider { + static let fakeValues = [CreateWorkoutItemPickerModel(id: 1, name: "one"), + CreateWorkoutItemPickerModel(id: 2, name: "two"), + CreateWorkoutItemPickerModel(id: 3, name: "three")] + + static var previews: some View { + CreateWorkoutItemPickerView(viewModel: CreateWorkoutItemPickerViewModel(allValues: fakeValues, selectedIds: [1]), completed: { selectedIds in + + }) + } +}