This commit is contained in:
Trey t
2023-06-15 23:44:34 -05:00
parent c2ea70305c
commit 544332c422
9 changed files with 2389 additions and 2170 deletions

View File

@@ -27,11 +27,12 @@
1CF65A592A3BF4B60042FFBD /* Muscle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A582A3BF4B60042FFBD /* Muscle.swift */; };
1CF65A5B2A3BF4BE0042FFBD /* Equipment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A5A2A3BF4BE0042FFBD /* Equipment.swift */; };
1CF65A5F2A3BF5A60042FFBD /* Equipment.json in Resources */ = {isa = PBXBuildFile; fileRef = 1CF65A5E2A3BF5A60042FFBD /* Equipment.json */; };
1CF65A612A3BF6020042FFBD /* CreateWorkoutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A602A3BF6020042FFBD /* CreateWorkoutView.swift */; };
1CF65A612A3BF6020042FFBD /* AddExerciseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A602A3BF6020042FFBD /* AddExerciseView.swift */; };
1CF65A632A3BF6A30042FFBD /* AllWorkoutsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A622A3BF6A30042FFBD /* AllWorkoutsView.swift */; };
1CF65A652A3BF6BE0042FFBD /* AllWorkouts.json in Resources */ = {isa = PBXBuildFile; fileRef = 1CF65A642A3BF6BE0042FFBD /* AllWorkouts.json */; };
1CF65A672A3BFF840042FFBD /* Exercises.json in Resources */ = {isa = PBXBuildFile; fileRef = 1CF65A662A3BFF840042FFBD /* Exercises.json */; };
1CF65A692A3C018F0042FFBD /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A682A3C018F0042FFBD /* AccountView.swift */; };
1CF65A6B2A3C1EAC0042FFBD /* CreateWorkoutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A6A2A3C1EAC0042FFBD /* CreateWorkoutView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -58,11 +59,12 @@
1CF65A582A3BF4B60042FFBD /* Muscle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Muscle.swift; sourceTree = "<group>"; };
1CF65A5A2A3BF4BE0042FFBD /* Equipment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Equipment.swift; sourceTree = "<group>"; };
1CF65A5E2A3BF5A60042FFBD /* Equipment.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Equipment.json; sourceTree = "<group>"; };
1CF65A602A3BF6020042FFBD /* CreateWorkoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateWorkoutView.swift; sourceTree = "<group>"; };
1CF65A602A3BF6020042FFBD /* AddExerciseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddExerciseView.swift; sourceTree = "<group>"; };
1CF65A622A3BF6A30042FFBD /* AllWorkoutsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllWorkoutsView.swift; sourceTree = "<group>"; };
1CF65A642A3BF6BE0042FFBD /* AllWorkouts.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = AllWorkouts.json; sourceTree = "<group>"; };
1CF65A662A3BFF840042FFBD /* Exercises.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Exercises.json; sourceTree = "<group>"; };
1CF65A682A3C018F0042FFBD /* AccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountView.swift; sourceTree = "<group>"; };
1CF65A6A2A3C1EAC0042FFBD /* CreateWorkoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateWorkoutView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -143,12 +145,13 @@
1CF65A3F2A3973840042FFBD /* Views */ = {
isa = PBXGroup;
children = (
1CF65A622A3BF6A30042FFBD /* AllWorkoutsView.swift */,
1CF65A602A3BF6020042FFBD /* CreateWorkoutView.swift */,
1CF65A2C2A3972840042FFBD /* MainView.swift */,
1CF65A4B2A39FDA20042FFBD /* WorkoutDetailView.swift */,
1CF65A4D2A39FF200042FFBD /* WorkoutDetailViewModel.swift */,
1CF65A3B2A3972CE0042FFBD /* ExternalWorkoutDetailView.swift */,
1CF65A622A3BF6A30042FFBD /* AllWorkoutsView.swift */,
1CF65A4B2A39FDA20042FFBD /* WorkoutDetailView.swift */,
1CF65A6A2A3C1EAC0042FFBD /* CreateWorkoutView.swift */,
1CF65A602A3BF6020042FFBD /* AddExerciseView.swift */,
1CF65A4D2A39FF200042FFBD /* WorkoutDetailViewModel.swift */,
1CF65A682A3C018F0042FFBD /* AccountView.swift */,
);
path = Views;
@@ -242,6 +245,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1CF65A6B2A3C1EAC0042FFBD /* CreateWorkoutView.swift in Sources */,
1CF65A262A3972840042FFBD /* Werkout_iosApp.swift in Sources */,
1CF65A3C2A3972CE0042FFBD /* ExternalWorkoutDetailView.swift in Sources */,
1CF65A632A3BF6A30042FFBD /* AllWorkoutsView.swift in Sources */,
@@ -258,7 +262,7 @@
1CF65A282A3972840042FFBD /* Persistence.swift in Sources */,
1CF65A5B2A3BF4BE0042FFBD /* Equipment.swift in Sources */,
1CF65A452A39FB550042FFBD /* Exercise.swift in Sources */,
1CF65A612A3BF6020042FFBD /* CreateWorkoutView.swift in Sources */,
1CF65A612A3BF6020042FFBD /* AddExerciseView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

File diff suppressed because it is too large Load Diff

View File

@@ -27,8 +27,8 @@
"id": 1856,
"created_at": "2023-06-13T02:28:05.326753Z",
"updated_at": "2023-06-13T02:28:05.327125Z",
"category": null,
"name": null
"exercise": 790,
"equipment": 1089
}
],
"audio_url": "/media/exercise_audio/Sprinter_Crunch.m4a",
@@ -109,8 +109,8 @@
"id": 1362,
"created_at": "2023-06-13T02:28:04.775223Z",
"updated_at": "2023-06-13T02:28:04.775809Z",
"category": null,
"name": null
"exercise": 603,
"equipment": 1089
}
],
"audio_url": "/media/exercise_audio/High_Plank.m4a",
@@ -205,8 +205,8 @@
"id": 1760,
"created_at": "2023-06-13T02:28:05.211760Z",
"updated_at": "2023-06-13T02:28:05.212193Z",
"category": null,
"name": null
"exercise": 142,
"equipment": 1091
}
],
"audio_url": "/media/exercise_audio/Single-Arm_Dumbbell_Suitcase_Carry.m4a",
@@ -268,8 +268,8 @@
"id": 1763,
"created_at": "2023-06-13T02:28:05.215315Z",
"updated_at": "2023-06-13T02:28:05.215849Z",
"category": null,
"name": null
"exercise": 370,
"equipment": 1091
}
],
"audio_url": "/media/exercise_audio/Single-Arm_Dumbbell_Suitcase_Deadlift.m4a",
@@ -324,8 +324,8 @@
"id": 1675,
"created_at": "2023-06-13T02:28:05.120220Z",
"updated_at": "2023-06-13T02:28:05.120555Z",
"category": null,
"name": null
"exercise": 1012,
"equipment": 1089
}
],
"audio_url": "/media/exercise_audio/Scissors.m4a",
@@ -413,8 +413,8 @@
"id": 1139,
"created_at": "2023-06-13T02:28:04.524659Z",
"updated_at": "2023-06-13T02:28:04.525069Z",
"category": null,
"name": null
"exercise": 495,
"equipment": 1089
}
],
"audio_url": "/media/exercise_audio/Chair_Pose.m4a",
@@ -502,8 +502,8 @@
"id": 1971,
"created_at": "2023-06-13T02:28:05.459371Z",
"updated_at": "2023-06-13T02:28:05.459787Z",
"category": null,
"name": null
"exercise": 448,
"equipment": 1106
}
],
"audio_url": "/media/exercise_audio/Wall_Slide_with_Lift_Off.m4a",
@@ -598,8 +598,8 @@
"id": 1149,
"created_at": "2023-06-13T02:28:04.535504Z",
"updated_at": "2023-06-13T02:28:04.535969Z",
"category": null,
"name": null
"exercise": 1045,
"equipment": 1089
}
],
"audio_url": "/media/exercise_audio/Crescent_Lunge.m4a",
@@ -661,8 +661,8 @@
"id": 1150,
"created_at": "2023-06-13T02:28:04.536605Z",
"updated_at": "2023-06-13T02:28:04.537010Z",
"category": null,
"name": null
"exercise": 1047,
"equipment": 1089
}
],
"audio_url": "/media/exercise_audio/Crescent_Lunge.m4a",

View File

@@ -6,7 +6,6 @@
//
import Foundation
struct Equipment: Codable {
let id: Int
let createdAt, updatedAt: String
@@ -19,3 +18,16 @@ struct Equipment: Codable {
case category, name
}
}
struct ExerciseEquipment: Codable, Hashable {
let id: Int
let createdAt, updatedAt: String
let exercise, equipment: Int
enum CodingKeys: String, CodingKey {
case id
case createdAt = "created_at"
case updatedAt = "updated_at"
case equipment, exercise
}
}

View File

@@ -32,10 +32,14 @@ struct ExerciseElement: Codable {
}
}
struct ExerciseExercise: Codable {
struct ExerciseExercise: Codable, Hashable {
static func == (lhs: ExerciseExercise, rhs: ExerciseExercise) -> Bool {
lhs.id == rhs.id
}
let id: Int
let muscles: [ExerciseMuscle]
let equipment: [Equipment]
let equipment: [ExerciseEquipment]
let audioURL, videoURL, createdAt, updatedAt: String
let name, description, side: String
let isTwoDumbbells, isTrackableDistance, isAlternating, isWeight: Bool

View File

@@ -12,7 +12,7 @@ struct Muscle: Codable {
let name: String
}
struct ExerciseMuscle: Codable {
struct ExerciseMuscle: Codable, Hashable {
let id: Int
let createdAt, updatedAt: String
let exercise, muscle: Int

View File

@@ -0,0 +1,227 @@
//
// CreateWorkout.swift
// Werkout_ios
//
// Created by Trey Tartt on 6/15/23.
//
import Foundation
import SwiftUI
struct AddExerciseView: View {
@State var exercises: [ExerciseExercise]?
@State var equipment: [Equipment]?
@State var muscles: [Muscle]?
@State var selectedMuscles = [Muscle]()
@State var selectedEquipment = [Equipment]()
@State var filteredExercises = [ExerciseExercise]()
@EnvironmentObject var bridgeModule: BridgeModule
@Environment(\.dismiss) var dismiss
var selectedWorkout: ((ExerciseExercise) -> Void)
var body: some View {
VStack {
if let muscles = muscles {
Button("toggle all", action: {
if self.selectedMuscles.count > 0 {
self.selectedMuscles.removeAll()
} else {
self.selectedMuscles.append(contentsOf: muscles)
}
filterExercises()
})
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)
}
if let equipment = equipment {
Button("toggle all", action: {
if self.selectedEquipment.count > 0 {
self.selectedEquipment.removeAll()
} else {
self.selectedEquipment.append(contentsOf: equipment)
}
filterExercises()
})
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)
}
List() {
ForEach(filteredExercises.indices, id: \.self) { i in
let obj = filteredExercises[i]
VStack {
Text(obj.name)
.frame(maxWidth: .infinity, alignment: .leading)
Text(obj.side)
.font(.footnote)
.frame(maxWidth: .infinity, alignment: .leading)
Text(obj.equipmentRequired)
.font(.footnote)
.frame(maxWidth: .infinity, alignment: .leading)
Text(obj.muscleGroups)
.font(.footnote)
.frame(maxWidth: .infinity, alignment: .leading)
}
.onTapGesture {
selectedWorkout(obj)
dismiss()
}
}
}
}
.onAppear{
parseMuscle()
parseEquipment()
parseExercises()
}
}
func filterExercises() {
if selectedMuscles.count == 0 {
filteredExercises = [ExerciseExercise]()
return
}
if selectedEquipment.count == 0 {
filteredExercises = [ExerciseExercise]()
return
}
guard let exercises = exercises,
let muscles = muscles,
let equipment = equipment else {
filteredExercises = [ExerciseExercise]()
return
}
filteredExercises = exercises.filter({ exercise in
var hasCorrectMuscles = false
if selectedMuscles.count == muscles.count {
hasCorrectMuscles = true
} else {
let exerciseMuscleIds = exercise.muscles.map({ $0.muscle })
let selctedMuscleIds = selectedMuscles.map({ $0.id })
if exerciseMuscleIds.contains(selctedMuscleIds) {
hasCorrectMuscles = true
}
}
var hasCorrectEquipment = false
if selectedEquipment.count == equipment.count {
hasCorrectEquipment = true
} else {
let exerciseEquipmentIds = exercise.equipment.map({ $0.equipment })
let selctedEquipmentIds = selectedEquipment.map({ $0.id })
if exerciseEquipmentIds.contains(selctedEquipmentIds) {
hasCorrectEquipment = true
}
}
return hasCorrectMuscles && hasCorrectEquipment
})
}
func parseExercises() {
if let filepath = Bundle.main.path(forResource: "Exercises", ofType: "json") {
do {
let data = try Data(NSData(contentsOfFile: filepath))
let workout = try JSONDecoder().decode([ExerciseExercise].self, from: data)
self.filteredExercises.append(contentsOf: workout)
self.exercises = workout
} catch {
print(error)
fatalError()
}
} else {
fatalError()
}
}
func parseEquipment() {
if let filepath = Bundle.main.path(forResource: "Equipment", ofType: "json") {
do {
let data = try Data(NSData(contentsOfFile: filepath))
let workout = try JSONDecoder().decode([Equipment].self, from: data)
self.equipment = workout
self.selectedEquipment.append(contentsOf: workout)
} catch {
print(error)
fatalError()
}
} else {
fatalError()
}
}
func parseMuscle() {
if let filepath = Bundle.main.path(forResource: "AllMuscles", ofType: "json") {
do {
let data = try Data(NSData(contentsOfFile: filepath))
let workout = try JSONDecoder().decode([Muscle].self, from: data)
self.muscles = workout
self.selectedMuscles.append(contentsOf: workout)
} catch {
print(error)
fatalError()
}
} else {
fatalError()
}
}
}

View File

@@ -26,8 +26,12 @@ struct AllWorkoutsView: View {
ForEach(workouts, id:\.name) { workout in
VStack {
Text(workout.name)
.font(.title2)
.frame(maxWidth: .infinity, alignment: .leading)
Text(workout.description ?? "")
.frame(maxWidth: .infinity, alignment: .leading)
}
.contentShape(Rectangle())
.onTapGesture {
selectedItem(workout: workout)
}

View File

@@ -1,75 +1,43 @@
//
// CreateWorkout.swift
// CreateWorkoutView.swift
// Werkout_ios
//
// Created by Trey Tartt on 6/15/23.
//
import Foundation
import SwiftUI
struct CreateWorkoutView: View {
@State var exercises: [ExerciseExercise]?
@State var equipment: [Equipment]?
@State var muscles: [Muscle]?
@EnvironmentObject var bridgeModule: BridgeModule
@State var exercises = [ExerciseExercise]()
@State private var showAddExercise = false
var body: some View {
VStack {
Text("exercises count: \(exercises?.count ?? -1)")
Text("equipment count: \(equipment?.count ?? -1)")
Text("muscles count: \(muscles?.count ?? -1)")
Button("add", action: {
showAddExercise.toggle()
})
List() {
ForEach(exercises.indices, id: \.self) { i in
let obj = exercises[i]
Text(obj.name)
}
}
}
.onAppear{
parseMuscle()
parseEquipment()
parseExercises()
.sheet(isPresented: $showAddExercise) {
AddExerciseView(selectedWorkout: { exercise in
addNewExercise(exercise: exercise)
})
}
}
func parseExercises() {
if let filepath = Bundle.main.path(forResource: "Exercises", ofType: "json") {
do {
let data = try Data(NSData(contentsOfFile: filepath))
let workout = try JSONDecoder().decode([ExerciseExercise].self, from: data)
self.exercises = workout
} catch {
print(error)
fatalError()
}
} else {
fatalError()
}
func addNewExercise(exercise: ExerciseExercise) {
exercises.append(exercise)
}
}
struct CreateWorkoutView_Previews: PreviewProvider {
static var previews: some View {
CreateWorkoutView()
}
func parseEquipment() {
if let filepath = Bundle.main.path(forResource: "Equipment", ofType: "json") {
do {
let data = try Data(NSData(contentsOfFile: filepath))
let workout = try JSONDecoder().decode([Equipment].self, from: data)
self.equipment = workout
} catch {
print(error)
fatalError()
}
} else {
fatalError()
}
}
func parseMuscle() {
if let filepath = Bundle.main.path(forResource: "AllMuscles", ofType: "json") {
do {
let data = try Data(NSData(contentsOfFile: filepath))
let workout = try JSONDecoder().decode([Muscle].self, from: data)
self.muscles = workout
} catch {
print(error)
fatalError()
}
} else {
fatalError()
}
}
}