From f40c40c9bba9c73a48136d47814616b655ccb75f Mon Sep 17 00:00:00 2001 From: Trey t Date: Sun, 25 Jun 2023 22:00:59 -0500 Subject: [PATCH] WIP --- Werkout_ios.xcodeproj/project.pbxproj | 12 +++ Werkout_ios/UserStore.swift | 26 ++++++- .../Views/AccountView/AccountView.swift | 11 +++ .../Views/AddExercise/AddExerciseView.swift | 44 ++++++----- .../Views/AllWorkouts/AllWorkoutsView.swift | 55 ++++++++------ .../CompletedWorkoutView.swift | 1 - .../CreateWorkout/CreateWorkoutMainView.swift | 56 +++++++------- .../CreateWorkoutSupersetActionsView.swift | 29 ++++---- Werkout_ios/Views/Login/LoginView.swift | 73 +++++++++++++++++++ 9 files changed, 220 insertions(+), 87 deletions(-) create mode 100644 Werkout_ios/Views/Login/LoginView.swift diff --git a/Werkout_ios.xcodeproj/project.pbxproj b/Werkout_ios.xcodeproj/project.pbxproj index cfbc046..edfcf28 100644 --- a/Werkout_ios.xcodeproj/project.pbxproj +++ b/Werkout_ios.xcodeproj/project.pbxproj @@ -9,6 +9,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 */; }; + 1C485C8A2A492BB400A6F896 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C485C892A492BB400A6F896 /* LoginView.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 */; }; @@ -97,6 +98,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 = ""; }; + 1C485C892A492BB400A6F896 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.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 = ""; }; @@ -167,6 +169,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 1C485C882A492BAA00A6F896 /* Login */ = { + isa = PBXGroup; + children = ( + 1C485C892A492BB400A6F896 /* LoginView.swift */, + ); + path = Login; + sourceTree = ""; + }; 1CF65A192A3972840042FFBD = { isa = PBXGroup; children = ( @@ -244,6 +254,7 @@ 1CF65A3F2A3973840042FFBD /* Views */ = { isa = PBXGroup; children = ( + 1C485C882A492BAA00A6F896 /* Login */, 1CF65A2C2A3972840042FFBD /* MainView.swift */, 1CF65A3B2A3972CE0042FFBD /* ExternalWorkoutDetailView.swift */, 1CF65A8B2A44B7590042FFBD /* AccountView */, @@ -483,6 +494,7 @@ 1CF65A2B2A3972840042FFBD /* Werkout_ios.xcdatamodeld in Sources */, 1CF65A2D2A3972840042FFBD /* MainView.swift in Sources */, 1CF65A7D2A41275D0042FFBD /* Network.swift in Sources */, + 1C485C8A2A492BB400A6F896 /* LoginView.swift in Sources */, 1CF65A732A3F60D20042FFBD /* CreateExerciseActionsView.swift in Sources */, 1CF65A832A42347D0042FFBD /* Extensions.swift in Sources */, 1CF65A282A3972840042FFBD /* Persistence.swift in Sources */, diff --git a/Werkout_ios/UserStore.swift b/Werkout_ios/UserStore.swift index 7b8f0be..8514b1c 100644 --- a/Werkout_ios/UserStore.swift +++ b/Werkout_ios/UserStore.swift @@ -8,10 +8,19 @@ import Foundation class UserStore: ObservableObject { + static let userDefaultsRegisteredUserKey = "registeredUserKey" static let shared = UserStore() @Published public private(set) var registeredUser: RegisteredUser? + init(registeredUser: RegisteredUser? = nil) { + self.registeredUser = registeredUser + if let data = UserDefaults.standard.data(forKey: UserStore.userDefaultsRegisteredUserKey), + let model = try? JSONDecoder().decode(RegisteredUser.self, from: data) { + self.registeredUser = model + } + } + public var token: String? { guard let token = registeredUser?.token else { return nil @@ -19,19 +28,28 @@ class UserStore: ObservableObject { return "Token \(token)" } - func login(completion: @escaping (Bool)-> Void) { - let postData = ["email": "user1@user1.com", "password":"test12345"] + func login(postData: [String: Any], completion: @escaping (Bool)-> Void) { LoginFetchable(postData: postData).fetch(completion: { result in switch result { case .success(let model): - self.registeredUser = model - completion(true) + DispatchQueue.main.async { + self.registeredUser = model + let data = try! JSONEncoder().encode(model) + UserDefaults.standard.set(data, forKey: UserStore.userDefaultsRegisteredUserKey) + completion(true) + } case .failure(let failure): completion(false) } }) } + func logout() { + self.registeredUser = nil + UserDefaults.standard.set(nil, forKey: UserStore.userDefaultsRegisteredUserKey) + NotificationCenter.default.post(name: NSNotification.Name("CreatedNewWorkout"), object: nil, userInfo: nil) + } + func setFakeUser() { self.registeredUser = PreviewWorkout.parseRegisterdUser() } diff --git a/Werkout_ios/Views/AccountView/AccountView.swift b/Werkout_ios/Views/AccountView/AccountView.swift index 4f56f30..b1135ec 100644 --- a/Werkout_ios/Views/AccountView/AccountView.swift +++ b/Werkout_ios/Views/AccountView/AccountView.swift @@ -49,6 +49,17 @@ struct AccountView: View { } } Spacer() + + Button("Logout", action: { + userStore.logout() + }) + .frame(maxWidth: .infinity, alignment: .center) + .frame(height: 44) + .foregroundColor(.white) + .background(.red) + .cornerRadius(8) + .padding() + .frame(maxWidth: .infinity) } .padding() .onAppear{ diff --git a/Werkout_ios/Views/AddExercise/AddExerciseView.swift b/Werkout_ios/Views/AddExercise/AddExerciseView.swift index f926f0e..26f0b03 100644 --- a/Werkout_ios/Views/AddExercise/AddExerciseView.swift +++ b/Werkout_ios/Views/AddExercise/AddExerciseView.swift @@ -24,9 +24,16 @@ struct AddExerciseView: View { var selectedWorkout: ((ExerciseExercise) -> Void) @State var createWorkoutItemPickerViewModel: CreateWorkoutItemPickerViewModel? @State var createWorkoutItemPickerViewType: CreateWorkoutItemPickerViewType? + @State var searchString: String = "" var body: some View { VStack { + exerciseView() + .padding(.top) + + TextField("Filter", text: $searchString) + .padding() + HStack { muscleView() .frame(maxWidth: .infinity) @@ -38,9 +45,6 @@ struct AddExerciseView: View { } .padding(.top) .frame(height: 44) - - exerciseView() - .padding(.top) } .onAppear{ if #function.hasPrefix("__preview") { @@ -190,25 +194,27 @@ struct AddExerciseView: View { List() { ForEach(filteredExercises.indices, id: \.self) { i in let obj = filteredExercises[i] - VStack { - Text(obj.name) - .frame(maxWidth: .infinity, alignment: .leading) - if obj.side.count > 0 { - Text(obj.side) + if searchString.isEmpty || obj.name.lowercased().contains(searchString.lowercased()) { + VStack { + Text(obj.name) + .frame(maxWidth: .infinity, alignment: .leading) + if obj.side.count > 0 { + 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) } - Text(obj.equipmentRequired) - .font(.footnote) - .frame(maxWidth: .infinity, alignment: .leading) - Text(obj.muscleGroups) - .font(.footnote) - .frame(maxWidth: .infinity, alignment: .leading) - } - .contentShape(Rectangle()) - .onTapGesture { - selectedWorkout(obj) - dismiss() + .contentShape(Rectangle()) + .onTapGesture { + selectedWorkout(obj) + dismiss() + } } } } diff --git a/Werkout_ios/Views/AllWorkouts/AllWorkoutsView.swift b/Werkout_ios/Views/AllWorkouts/AllWorkoutsView.swift index 078f147..0b22005 100644 --- a/Werkout_ios/Views/AllWorkouts/AllWorkoutsView.swift +++ b/Werkout_ios/Views/AllWorkouts/AllWorkoutsView.swift @@ -21,6 +21,8 @@ struct AllWorkoutsView: View { } } + @State private var showLoginView = false + let pub = NotificationCenter.default.publisher(for: NSNotification.Name("CreatedNewWorkout")) var body: some View { @@ -45,34 +47,45 @@ struct AllWorkoutsView: View { Text("no workouts") } }.onAppear{ - if needsUpdating { - UserStore.shared.login(completion: { success in - if success { - DataStore.shared.fetchAllData() - - AllWorkoutFetchable().fetch(completion: { result in - needsUpdating = false - switch result { - case .success(let model): - DispatchQueue.main.async { - self.workouts = model - } - case .failure(let failure): - fatalError("shit broke") - } - }) - } else { - fatalError("shit broke") - } - }) - } + maybeUpdateShit() } .sheet(item: $selectedWorkout) { item in let viewModel = WorkoutDetailViewModel(workout: item) WorkoutDetailView(viewModel: viewModel) } + .sheet(isPresented: $showLoginView) { + LoginView(completion: { + self.needsUpdating = true + maybeUpdateShit() + }) + } .onReceive(pub) { (output) in self.needsUpdating = true + maybeUpdateShit() + } + } + + func maybeUpdateShit() { + if UserStore.shared.token != nil{ + if needsUpdating { + DataStore.shared.fetchAllData() + + AllWorkoutFetchable().fetch(completion: { result in + needsUpdating = false + switch result { + case .success(let model): + DispatchQueue.main.async { + self.workouts = model + } + case .failure(_): + fatalError("shit broke") + } + }) + } else { + fatalError("shit broke") + } + } else { + showLoginView = true } } } diff --git a/Werkout_ios/Views/CompletedWorkout/CompletedWorkoutView.swift b/Werkout_ios/Views/CompletedWorkout/CompletedWorkoutView.swift index 9d5efdd..8c59e03 100644 --- a/Werkout_ios/Views/CompletedWorkout/CompletedWorkoutView.swift +++ b/Werkout_ios/Views/CompletedWorkout/CompletedWorkoutView.swift @@ -43,7 +43,6 @@ struct CompletedWorkoutView: View { .overlay(RoundedRectangle(cornerRadius: 16).stroke(Color(uiColor: .clear))).background(Color(uiColor: .init(red: 200/255, green: 200/255, blue: 200/255, alpha: 0.2))) .cornerRadius(8) - Spacer() Button("Upload", action: { diff --git a/Werkout_ios/Views/CreateWorkout/CreateWorkoutMainView.swift b/Werkout_ios/Views/CreateWorkout/CreateWorkoutMainView.swift index 3b088dd..48acc92 100644 --- a/Werkout_ios/Views/CreateWorkout/CreateWorkoutMainView.swift +++ b/Werkout_ios/Views/CreateWorkout/CreateWorkoutMainView.swift @@ -17,33 +17,6 @@ struct CreateWorkoutMainView: View { TextField("Title", text: $viewModel.title) .padding() - HStack { - Button("Add Superset", action: { - viewModel.addNewSuperset() - }) - .frame(maxWidth: .infinity, alignment: .center) - .frame(height: 44) - .foregroundColor(.blue) - .background(.yellow) - .cornerRadius(8) - .padding() - .frame(maxWidth: .infinity) - - Divider() - - Button("Done", action: { - viewModel.uploadWorkout() - }) - .frame(maxWidth: .infinity, alignment: .center) - .frame(height: 44) - .foregroundColor(.white) - .background(.blue) - .cornerRadius(8) - .padding() - .frame(maxWidth: .infinity) - } - .frame(height: 44) - List() { ForEach($viewModel.superSets, id: \.id) { superset in Section { @@ -86,6 +59,35 @@ struct CreateWorkoutMainView: View { } .listRowSeparator(.hidden) } + + HStack { + Button("Add Superset", action: { + viewModel.addNewSuperset() + }) + .frame(maxWidth: .infinity, alignment: .center) + .frame(height: 44) + .foregroundColor(.blue) + .background(.yellow) + .cornerRadius(8) + .padding() + .frame(maxWidth: .infinity) + + Divider() + + Button("Done", action: { + viewModel.uploadWorkout() + }) + .frame(maxWidth: .infinity, alignment: .center) + .frame(height: 44) + .foregroundColor(.white) + .background(.blue) + .cornerRadius(8) + .padding() + .frame(maxWidth: .infinity) + .disabled(viewModel.title.isEmpty) + } + .frame(height: 44) + .padding(.bottom) } .sheet(isPresented: $showAddExercise) { AddExerciseView(selectedWorkout: { exercise in diff --git a/Werkout_ios/Views/CreateWorkout/CreateWorkoutSupersetActionsView.swift b/Werkout_ios/Views/CreateWorkout/CreateWorkoutSupersetActionsView.swift index d67334b..2d7e7fa 100644 --- a/Werkout_ios/Views/CreateWorkout/CreateWorkoutSupersetActionsView.swift +++ b/Werkout_ios/Views/CreateWorkout/CreateWorkoutSupersetActionsView.swift @@ -22,26 +22,25 @@ struct CreateWorkoutSupersetActionsView: View { Text("Add exercise") .padding() } - .frame(maxWidth: .infinity) .foregroundColor(.white) .background(.green) .cornerRadius(10) .frame(maxWidth: .infinity, alignment: .center) + .buttonStyle(BorderlessButtonStyle()) - // Button(action: { - // if let selectedCreateWorkoutSuperSet = selectedCreateWorkoutSuperSet { - // viewModel.delete(superset: selectedCreateWorkoutSuperSet) - // viewModel.objectWillChange.send() - // } - // }) { - // Text("Delete superset") - // .padding() - // } - // .frame(maxWidth: .infinity) - // .foregroundColor(.white) - // .background(.red) - // .cornerRadius(10) - // .frame(maxWidth: .infinity, alignment: .center) + Button(action: { + viewModel.delete(superset: workoutSuperSet) + viewModel.objectWillChange.send() + + }) { + Text("Delete superset") + .padding() + } + .foregroundColor(.white) + .background(.red) + .cornerRadius(10) + .frame(maxWidth: .infinity, alignment: .center) + .buttonStyle(BorderlessButtonStyle()) } } } diff --git a/Werkout_ios/Views/Login/LoginView.swift b/Werkout_ios/Views/Login/LoginView.swift new file mode 100644 index 0000000..de6b7e5 --- /dev/null +++ b/Werkout_ios/Views/Login/LoginView.swift @@ -0,0 +1,73 @@ +// +// LoginView.swift +// Werkout_ios +// +// Created by Trey Tartt on 6/25/23. +// + +import SwiftUI + +struct LoginView: View { + @State var email: String = "" + @State var password: String = "" + @Environment(\.dismiss) var dismiss + let completion: (() -> Void) + + var body: some View { + VStack { + Text("Login") + .font(.title) + + TextField("Email", text: $email) + .autocapitalization(.none) + .frame(height: 55) + .textFieldStyle(PlainTextFieldStyle()) + .padding([.horizontal], 4) + .overlay(RoundedRectangle(cornerRadius: 16).stroke(Color(uiColor: .clear))).background(Color(uiColor: .init(red: 200/255, green: 200/255, blue: 200/255, alpha: 0.2))) + .cornerRadius(8) + + TextField("Password", text: $password) + .autocapitalization(.none) + .frame(height: 55) + .textFieldStyle(PlainTextFieldStyle()) + .padding([.horizontal], 4) + .overlay(RoundedRectangle(cornerRadius: 16).stroke(Color(uiColor: .clear))).background(Color(uiColor: .init(red: 200/255, green: 200/255, blue: 200/255, alpha: 0.2))) + .cornerRadius(8) + + Button("Login", action: { + login() + }) + .frame(maxWidth: .infinity, alignment: .center) + .frame(height: 44) + .foregroundColor(.blue) + .background(.yellow) + .cornerRadius(8) + .padding() + .frame(maxWidth: .infinity) + + Spacer() + } + .padding() + } + + func login() { + let postData = [ + "email": email, + "password": password + ] + UserStore.shared.login(postData: postData, completion: { success in + if success { + completion() + dismiss() + } + }) + } +} + +struct LoginView_Previews: PreviewProvider { + static var previews: some View { + LoginView(completion: { + + }) + } +}