add apple tv app
This commit is contained in:
110
iphone/Werkout_ios/subview/ActionsView.swift
Normal file
110
iphone/Werkout_ios/subview/ActionsView.swift
Normal file
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// ActionsView.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 7/7/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ActionsView: View {
|
||||
@ObservedObject var bridgeModule = BridgeModule.shared
|
||||
var completedWorkout: (() -> Void)?
|
||||
var planWorkout: ((Workout) -> Void)?
|
||||
|
||||
var workout: Workout
|
||||
@Environment(\.dismiss) var dismiss
|
||||
var showAddToCalendar: Bool
|
||||
var startWorkoutAction: (() -> Void)
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
if bridgeModule.isInWorkout == false {
|
||||
Button(action: {
|
||||
bridgeModule.resetCurrentWorkout()
|
||||
dismiss()
|
||||
}, label: {
|
||||
Image(systemName: "xmark.octagon.fill")
|
||||
.font(.title)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
})
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(.red)
|
||||
.foregroundColor(.white)
|
||||
|
||||
if showAddToCalendar {
|
||||
Button(action: {
|
||||
planWorkout?(workout)
|
||||
}, label: {
|
||||
Image(systemName: "calendar.badge.plus")
|
||||
.font(.title)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
})
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(.blue)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
startWorkoutAction()
|
||||
}, label: {
|
||||
Image(systemName: "arrowtriangle.forward.fill")
|
||||
.font(.title)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
})
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(.green)
|
||||
.foregroundColor(.white)
|
||||
} else {
|
||||
Button(action: {
|
||||
nextExercise()
|
||||
}, label: {
|
||||
Image(systemName: "arrow.forward")
|
||||
.font(.title)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
})
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(.green)
|
||||
.foregroundColor(.white)
|
||||
|
||||
Button(action: {
|
||||
bridgeModule.pauseWorkout()
|
||||
}, label: {
|
||||
bridgeModule.isPaused ?
|
||||
Image(systemName: "play.circle.fill")
|
||||
.font(.title)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
:
|
||||
Image(systemName: "pause.circle.fill")
|
||||
.font(.title)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
})
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(bridgeModule.isPaused ? .mint : .yellow)
|
||||
.foregroundColor(.white)
|
||||
|
||||
Button(action: {
|
||||
completedWorkout?()
|
||||
}, label: {
|
||||
Image(systemName: "checkmark")
|
||||
.font(.title)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
})
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(.blue)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func nextExercise() {
|
||||
bridgeModule.nextExercise()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct ActionsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ActionsView(workout: PreviewData.workout(), showAddToCalendar: true, startWorkoutAction: {})
|
||||
}
|
||||
}
|
||||
56
iphone/Werkout_ios/subview/AllEquipmentView.swift
Normal file
56
iphone/Werkout_ios/subview/AllEquipmentView.swift
Normal file
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// AllEquipmentview.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 6/16/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct AllEquipmentView: View {
|
||||
@Binding var selectedEquipment: [Equipment]
|
||||
@State var createWorkoutItemPickerViewModel: CreateWorkoutItemPickerViewModel?
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if let _ = DataStore.shared.allEquipment {
|
||||
Text("Select Equipment")
|
||||
.foregroundColor(.cyan)
|
||||
Text("\(selectedEquipment.count) Selected")
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
if let equipment = DataStore.shared.allEquipment {
|
||||
var createWorkoutItemPickerModels = [CreateWorkoutItemPickerModel]()
|
||||
equipment.forEach({
|
||||
let model = CreateWorkoutItemPickerModel(id: $0.id,
|
||||
name: $0.name.lowercased())
|
||||
createWorkoutItemPickerModels.append(model)
|
||||
})
|
||||
createWorkoutItemPickerModels = createWorkoutItemPickerModels.sorted(by: {
|
||||
$0.name < $1.name
|
||||
})
|
||||
let selectedIds = selectedEquipment.map { $0.id }
|
||||
createWorkoutItemPickerViewModel = CreateWorkoutItemPickerViewModel(allValues: createWorkoutItemPickerModels, selectedIds: selectedIds)
|
||||
}
|
||||
}
|
||||
.sheet(item: $createWorkoutItemPickerViewModel) { item in
|
||||
CreateWorkoutItemPickerView(viewModel: item, completed: { selectedids in
|
||||
if let equipment = DataStore.shared.allEquipment {
|
||||
selectedEquipment.removeAll()
|
||||
for id in selectedids {
|
||||
if let equipment = equipment.first(where: {
|
||||
$0.id == id
|
||||
}) {
|
||||
selectedEquipment.append(equipment)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#Preview {
|
||||
// AllEquipmentview()
|
||||
//}
|
||||
92
iphone/Werkout_ios/subview/AllExerciseView.swift
Normal file
92
iphone/Werkout_ios/subview/AllExerciseView.swift
Normal file
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// AllExerciseView.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 6/16/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AVKit
|
||||
|
||||
struct AllExerciseView: View {
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@State var searchString: String = ""
|
||||
@Binding var filteredExercises: [Exercise]
|
||||
var selectedExercise: ((Exercise) -> Void)
|
||||
|
||||
@State var avPlayer = AVPlayer(url: URL(string: "https://dev.werkout.fitness/media/exercise_videos/2_Dumbbell_Lateral_Lunges.mp4")!)
|
||||
@State var videoExercise: Exercise? {
|
||||
didSet {
|
||||
if let viddd = self.videoExercise?.videoURL,
|
||||
let url = URL(string: BaseURLs.currentBaseURL + viddd) {
|
||||
self.avPlayer = AVPlayer(url: url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
TextField("Filter", text: $searchString)
|
||||
.padding()
|
||||
|
||||
List() {
|
||||
ForEach(filteredExercises, id: \.self) { exercise in
|
||||
if searchString.isEmpty || (exercise.name.lowercased().contains(searchString.lowercased()) || (exercise.muscleGroups ?? "").lowercased().contains(searchString.lowercased())) {
|
||||
HStack {
|
||||
VStack {
|
||||
Text(exercise.name)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
if exercise.side != nil && !exercise.side!.isEmpty {
|
||||
Text(exercise.side!)
|
||||
.font(.footnote)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
if exercise.equipmentRequired != nil && !exercise.equipmentRequired!.isEmpty {
|
||||
Text(exercise.spacedEquipmentRequired)
|
||||
.font(.footnote)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
if exercise.muscleGroups != nil && !exercise.muscleGroups!.isEmpty {
|
||||
Text(exercise.spacedMuscleGroups)
|
||||
.font(.footnote)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
selectedExercise(exercise)
|
||||
dismiss()
|
||||
}
|
||||
Button(action: {
|
||||
videoExercise = exercise
|
||||
}) {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(.blue)
|
||||
.frame(width: 33, height: 33)
|
||||
Image(systemName: "video.fill")
|
||||
.frame(width: 33, height: 33)
|
||||
.foregroundColor(.white )
|
||||
}
|
||||
}
|
||||
.frame(width: 33, height: 33)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.sheet(item: $videoExercise) { exercise in
|
||||
PlayerView(player: $avPlayer)
|
||||
.onAppear{
|
||||
avPlayer.play()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//#Preview {
|
||||
// AllExerciseView()
|
||||
//}
|
||||
66
iphone/Werkout_ios/subview/AllMusclesView.swift
Normal file
66
iphone/Werkout_ios/subview/AllMusclesView.swift
Normal file
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// AllMusclesView.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 6/16/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct AllMusclesView: View {
|
||||
@Binding var selectedMuscles: [Muscle]
|
||||
@State var createWorkoutItemPickerViewModel: CreateWorkoutItemPickerViewModel?
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if let _ = DataStore.shared.allMuscles {
|
||||
Text("Select Muscles")
|
||||
.foregroundColor(.cyan)
|
||||
Text("\(selectedMuscles.count) Selected")
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
if let muscles = DataStore.shared.allMuscles {
|
||||
var createWorkoutItemPickerModels = [CreateWorkoutItemPickerModel]()
|
||||
muscles.forEach({
|
||||
let model = CreateWorkoutItemPickerModel(id: $0.id, name: $0.name.lowercased())
|
||||
createWorkoutItemPickerModels.append(model)
|
||||
})
|
||||
createWorkoutItemPickerModels = createWorkoutItemPickerModels.sorted(by: {
|
||||
$0.name < $1.name
|
||||
})
|
||||
let selectedIds = selectedMuscles.map { $0.id }
|
||||
createWorkoutItemPickerViewModel = CreateWorkoutItemPickerViewModel(allValues: createWorkoutItemPickerModels, selectedIds: selectedIds)
|
||||
}
|
||||
}
|
||||
.onAppear{
|
||||
if #function.hasPrefix("__preview") {
|
||||
DataStore.shared.setupFakeData()
|
||||
}
|
||||
guard let _ = DataStore.shared.allExercise,
|
||||
let muscles = DataStore.shared.allMuscles,
|
||||
let _ = DataStore.shared.allEquipment else {
|
||||
return
|
||||
}
|
||||
selectedMuscles = muscles
|
||||
}
|
||||
.sheet(item: $createWorkoutItemPickerViewModel) { item in
|
||||
CreateWorkoutItemPickerView(viewModel: item, completed: { selectedids in
|
||||
if let muscles = DataStore.shared.allMuscles {
|
||||
selectedMuscles.removeAll()
|
||||
for id in selectedids {
|
||||
if let muscle = muscles.first(where: {
|
||||
$0.id == id
|
||||
}) {
|
||||
selectedMuscles.append(muscle)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#Preview {
|
||||
// AllMusclesView()
|
||||
//}
|
||||
37
iphone/Werkout_ios/subview/AllWorkoutPickerView.swift
Normal file
37
iphone/Werkout_ios/subview/AllWorkoutPickerView.swift
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// AllWorkoutPickerView.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 7/7/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct AllWorkoutPickerView: View {
|
||||
var mainViews: [MainViewTypes]
|
||||
@Binding var selectedSegment: MainViewTypes
|
||||
@StateObject var bridgeModule = BridgeModule.shared
|
||||
var showCurrentWorkout: (() -> Void)
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Picker("", selection: $selectedSegment) {
|
||||
ForEach(mainViews, id: \.self) { viewType in
|
||||
Text(viewType.title)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
.padding([.top, .leading, .trailing])
|
||||
|
||||
if bridgeModule.isInWorkout {
|
||||
Button(action: {
|
||||
showCurrentWorkout()
|
||||
}, label: {
|
||||
Image(systemName: "figure.strengthtraining.traditional")
|
||||
.padding(.trailing)
|
||||
})
|
||||
.tint(Color("appColor"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
71
iphone/Werkout_ios/subview/CompletedWorkoutsView.swift
Normal file
71
iphone/Werkout_ios/subview/CompletedWorkoutsView.swift
Normal file
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// CompletedWorkoutsView.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 6/16/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct CompletedWorkoutsView: View {
|
||||
@State var completedWorkouts: [CompletedWorkout]?
|
||||
@State var showCompletedWorkouts: Bool = false
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
if let completedWorkouts = completedWorkouts {
|
||||
|
||||
Divider()
|
||||
Text("Workout History:")
|
||||
HStack {
|
||||
Text("Number of workouts:")
|
||||
Text("\(completedWorkouts.count)")
|
||||
}
|
||||
|
||||
if let lastWorkout = completedWorkouts.last {
|
||||
HStack {
|
||||
Text("Last workout:")
|
||||
Text(lastWorkout.workoutStartTime)
|
||||
}
|
||||
|
||||
Button("View All Workouts", action: {
|
||||
showCompletedWorkouts = true
|
||||
})
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
.frame(height: 44)
|
||||
.foregroundColor(.blue)
|
||||
.background(.yellow)
|
||||
.cornerRadius(8)
|
||||
.padding()
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
|
||||
} else {
|
||||
Text("loading completed workouts")
|
||||
}
|
||||
}
|
||||
.onAppear{
|
||||
fetchCompletedWorkouts()
|
||||
}
|
||||
.sheet(isPresented: $showCompletedWorkouts) {
|
||||
if let completedWorkouts = completedWorkouts {
|
||||
WorkoutHistoryView(completedWorkouts: completedWorkouts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fetchCompletedWorkouts() {
|
||||
CompletedWorkoutFetchable().fetch(completion: { result in
|
||||
switch result {
|
||||
case .success(let model):
|
||||
completedWorkouts = model
|
||||
case .failure(let failure):
|
||||
fatalError(failure.localizedDescription)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
CompletedWorkoutsView()
|
||||
}
|
||||
30
iphone/Werkout_ios/subview/CountdownView.swift
Normal file
30
iphone/Werkout_ios/subview/CountdownView.swift
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// CountdownView.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 7/7/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct CountdownView: View {
|
||||
@StateObject var bridgeModule = BridgeModule.shared
|
||||
|
||||
var body: some View {
|
||||
if let duration = bridgeModule.currentExerciseInfo.currentExercise?.duration,
|
||||
duration > 0 {
|
||||
HStack {
|
||||
if bridgeModule.currentExerciseTimeLeft >= 0 && duration > bridgeModule.currentExerciseTimeLeft {
|
||||
ProgressView(value: Float(bridgeModule.currentExerciseTimeLeft), total: Float(duration))
|
||||
.tint(Color("appColor"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CountdownView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
CountdownView()
|
||||
}
|
||||
}
|
||||
81
iphone/Werkout_ios/subview/ExtCountdownView.swift
Normal file
81
iphone/Werkout_ios/subview/ExtCountdownView.swift
Normal file
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// ExtCountdownView.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 6/17/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ExtCountdownView: View {
|
||||
@StateObject var bridgeModule = BridgeModule.shared
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { metrics in
|
||||
VStack {
|
||||
if let currenExercise = bridgeModule.currentExerciseInfo.currentExercise {
|
||||
HStack {
|
||||
Text(currenExercise.exercise.extName)
|
||||
.font(.system(size: 200))
|
||||
.scaledToFit()
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(1)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
if bridgeModule.currentWorkoutRunTimeInSeconds > -1 {
|
||||
Text("\(Double(bridgeModule.currentWorkoutRunTimeInSeconds).asString(style: .positional))")
|
||||
.font(Font.system(size: 100))
|
||||
.scaledToFit()
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(1)
|
||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||
.padding(.trailing, 100)
|
||||
}
|
||||
}
|
||||
.frame(height: metrics.size.height * 0.5)
|
||||
|
||||
HStack {
|
||||
if let duration = currenExercise.duration,
|
||||
duration > 0 {
|
||||
ProgressView(value: Float(bridgeModule.currentExerciseTimeLeft), total: Float(duration))
|
||||
.scaleEffect(x: 1, y: 6, anchor: .center)
|
||||
Text("\(bridgeModule.currentExerciseTimeLeft)")
|
||||
.font(Font.system(size: 100))
|
||||
.scaledToFit()
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(1)
|
||||
.padding(.leading)
|
||||
.padding(.trailing, 100)
|
||||
}
|
||||
|
||||
if let reps = currenExercise.reps,
|
||||
reps > 0 {
|
||||
Text(" X \(reps)")
|
||||
.font(Font.system(size: 100))
|
||||
.scaledToFit()
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(1)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
if let weight = currenExercise.weight,
|
||||
weight > 0 {
|
||||
Text(" @ \(weight)")
|
||||
.font(Font.system(size: 100))
|
||||
.scaledToFit()
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(1)
|
||||
.padding(.trailing, 100)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
.frame(height: metrics.size.height * 0.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ExtCountdownView()
|
||||
}
|
||||
76
iphone/Werkout_ios/subview/ExtExerciseList.swift
Normal file
76
iphone/Werkout_ios/subview/ExtExerciseList.swift
Normal file
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// ExtExerciseList.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 6/17/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ExtExerciseList: View {
|
||||
var workout: Workout
|
||||
var allSupersetExecerciseIndex: Int
|
||||
|
||||
var body: some View {
|
||||
if let allSupersetExecercise = workout.allSupersetExecercise {
|
||||
ZStack {
|
||||
ScrollViewReader { proxy in
|
||||
List() {
|
||||
ForEach(allSupersetExecercise.indices, id: \.self) { supersetExecerciseIdx in
|
||||
let supersetExecercise = allSupersetExecercise[supersetExecerciseIdx]
|
||||
HStack {
|
||||
if supersetExecerciseIdx == allSupersetExecerciseIndex {
|
||||
Image(systemName: "figure.run")
|
||||
.foregroundColor(Color("appColor"))
|
||||
.font(Font.system(size: 55))
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(1)
|
||||
}
|
||||
|
||||
Text(supersetExecercise.exercise.name)
|
||||
.font(Font.system(size: 55))
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(3)
|
||||
.padding()
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.id(supersetExecerciseIdx)
|
||||
}
|
||||
}
|
||||
.onChange(of: allSupersetExecerciseIndex, perform: { newValue in
|
||||
withAnimation {
|
||||
proxy.scrollTo(allSupersetExecerciseIndex, anchor: .top)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
VStack {
|
||||
Text("\(allSupersetExecerciseIndex+1)/\(workout.allSupersetExecercise?.count ?? 0)")
|
||||
.font(Font.system(size: 55))
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(1)
|
||||
.padding()
|
||||
.bold()
|
||||
.foregroundColor(.white)
|
||||
.background(
|
||||
Capsule()
|
||||
.strokeBorder(Color.black, lineWidth: 0.8)
|
||||
.background(Color(uiColor: UIColor(red: 148/255,
|
||||
green: 0,
|
||||
blue: 211/255,
|
||||
alpha: 0.5)))
|
||||
.clipped()
|
||||
)
|
||||
.clipShape(Capsule())
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#Preview {
|
||||
// ExtExerciseList()
|
||||
//}
|
||||
42
iphone/Werkout_ios/subview/InfoView.swift
Normal file
42
iphone/Werkout_ios/subview/InfoView.swift
Normal file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// InfoView.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 7/7/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct InfoView: View {
|
||||
@ObservedObject var bridgeModule = BridgeModule.shared
|
||||
var workout: Workout
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text(workout.name)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.font(.title3)
|
||||
.padding()
|
||||
|
||||
if let desc = workout.description {
|
||||
Text(desc)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.font(.body)
|
||||
.padding([.leading, .trailing])
|
||||
}
|
||||
|
||||
if let estimatedTime = workout.estimatedTime {
|
||||
Text(estimatedTime.asString(style: .abbreviated))
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.font(.body)
|
||||
.padding([.leading, .trailing])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct InfoView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
InfoView(workout: PreviewData.workout())
|
||||
}
|
||||
}
|
||||
43
iphone/Werkout_ios/subview/Logoutview.swift
Normal file
43
iphone/Werkout_ios/subview/Logoutview.swift
Normal file
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// Logoutview.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 6/16/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct Logoutview: View {
|
||||
@ObservedObject var userStore = UserStore.shared
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Button("Logout", action: {
|
||||
userStore.logout()
|
||||
})
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
.frame(height: 44)
|
||||
.foregroundColor(.white)
|
||||
.background(.red)
|
||||
.cornerRadius(8)
|
||||
.padding()
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
Button(action: {
|
||||
userStore.refreshUserData()
|
||||
}, label: {
|
||||
Image(systemName: "arrow.triangle.2.circlepath")
|
||||
})
|
||||
.frame(width: 44, height: 44)
|
||||
.foregroundColor(.white)
|
||||
.background(.green)
|
||||
.cornerRadius(8)
|
||||
.padding()
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
Logoutview()
|
||||
}
|
||||
34
iphone/Werkout_ios/subview/NameView.swift
Normal file
34
iphone/Werkout_ios/subview/NameView.swift
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// NameView.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 6/16/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct NameView: View {
|
||||
@ObservedObject var userStore = UserStore.shared
|
||||
|
||||
var body: some View {
|
||||
if let registeredUser = userStore.registeredUser {
|
||||
if let nickName = registeredUser.nickName {
|
||||
Text(nickName)
|
||||
.font(.title)
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text(registeredUser.firstName ?? "-")
|
||||
Text(registeredUser.lastName ?? "-")
|
||||
}
|
||||
|
||||
if let email = registeredUser.email {
|
||||
Text(email)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
NameView()
|
||||
}
|
||||
18
iphone/Werkout_ios/subview/OvalTextFieldStyle.swift
Normal file
18
iphone/Werkout_ios/subview/OvalTextFieldStyle.swift
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// OvalTextFieldStyle.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 7/6/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct OvalTextFieldStyle: TextFieldStyle {
|
||||
func _body(configuration: TextField<Self._Label>) -> some View {
|
||||
configuration
|
||||
.padding(10)
|
||||
.background(LinearGradient(gradient: Gradient(colors: [Color(uiColor: .secondarySystemBackground), Color(uiColor: .secondarySystemBackground)]), startPoint: .topLeading, endPoint: .bottomTrailing))
|
||||
.cornerRadius(20)
|
||||
.shadow(color: Color(red: 120/255, green: 120/255, blue: 120/255, opacity: 1), radius: 5)
|
||||
}
|
||||
}
|
||||
127
iphone/Werkout_ios/subview/PlayerUIView.swift
Normal file
127
iphone/Werkout_ios/subview/PlayerUIView.swift
Normal file
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// PlayerUIView.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 7/5/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AVKit
|
||||
|
||||
class PlayerUIView: UIView {
|
||||
|
||||
// MARK: Class Property
|
||||
|
||||
let playerLayer = AVPlayerLayer()
|
||||
|
||||
// MARK: Init
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
init(player: AVPlayer) {
|
||||
super.init(frame: .zero)
|
||||
self.playerSetup(player: player)
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
// MARK: Life-Cycle
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
playerLayer.frame = bounds
|
||||
}
|
||||
|
||||
// MARK: Class Methods
|
||||
|
||||
private func playerSetup(player: AVPlayer) {
|
||||
playerLayer.player = player
|
||||
player.actionAtItemEnd = .none
|
||||
layer.addSublayer(playerLayer)
|
||||
|
||||
self.setObserver()
|
||||
}
|
||||
|
||||
func setObserver() {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidReachEnd(notification:)),
|
||||
name: .AVPlayerItemDidPlayToEndTime,
|
||||
object: playerLayer.player?.currentItem)
|
||||
}
|
||||
|
||||
@objc func playerItemDidReachEnd(notification: Notification) {
|
||||
if let playerItem = notification.object as? AVPlayerItem {
|
||||
playerItem.seek(to: .zero, completionHandler: nil)
|
||||
self.playerLayer.player?.play()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PlayerView: UIViewRepresentable {
|
||||
|
||||
@Binding var player: AVPlayer
|
||||
|
||||
func makeUIView(context: Context) -> PlayerUIView {
|
||||
return PlayerUIView(player: player)
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: PlayerUIView, context: UIViewRepresentableContext<PlayerView>) {
|
||||
uiView.playerLayer.player = player
|
||||
|
||||
//Add player observer.
|
||||
uiView.setObserver()
|
||||
}
|
||||
}
|
||||
|
||||
class VideoURLCreator {
|
||||
class func otherVideoType(forVideoURL videoURL: URL) -> ThotStyle {
|
||||
var otherVideoStyle = ThotStyle.never
|
||||
if videoURL.absoluteString.contains("exercise_videos") {
|
||||
otherVideoStyle = .always
|
||||
}
|
||||
return otherVideoStyle
|
||||
}
|
||||
|
||||
class func videoURL(thotStyle: ThotStyle, gender: String, defaultVideoURLStr: String?, exerciseName: String?, workout: Workout?) -> URL? {
|
||||
var urlString: String?
|
||||
|
||||
if UserStore.shared.registeredUser?.NSFWValue ?? false {
|
||||
switch thotStyle {
|
||||
case .always:
|
||||
urlString = DataStore.shared.randomVideoFor(gender: gender)
|
||||
case .never:
|
||||
urlString = defaultVideoURLStr
|
||||
case .recovery:
|
||||
if exerciseName?.lowercased() == "recover" {
|
||||
urlString = DataStore.shared.randomVideoFor(gender: gender)
|
||||
} else {
|
||||
urlString = defaultVideoURLStr
|
||||
}
|
||||
case .random:
|
||||
if Bool.random() {
|
||||
urlString = DataStore.shared.randomVideoFor(gender: gender)
|
||||
} else {
|
||||
urlString = defaultVideoURLStr
|
||||
}
|
||||
case .off:
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
urlString = defaultVideoURLStr
|
||||
}
|
||||
|
||||
if let urlString = urlString,
|
||||
let url = URL(string: BaseURLs.currentBaseURL + urlString) {
|
||||
return url
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
22
iphone/Werkout_ios/subview/ShowNextUpView.swift
Normal file
22
iphone/Werkout_ios/subview/ShowNextUpView.swift
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// ShowNextUpView.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 6/16/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ShowNextUpView: View {
|
||||
@AppStorage(Constants.extShowNextVideo) private var extShowNextVideo: Bool = false
|
||||
|
||||
var body: some View {
|
||||
Toggle(isOn: $extShowNextVideo, label: {
|
||||
Text("Show next up video")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ShowNextUpView()
|
||||
}
|
||||
57
iphone/Werkout_ios/subview/ThotPreferenceView.swift
Normal file
57
iphone/Werkout_ios/subview/ThotPreferenceView.swift
Normal file
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// ThosPreferenceView.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 6/16/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ThotPreferenceView: View {
|
||||
@ObservedObject var userStore = UserStore.shared
|
||||
@AppStorage(Constants.phoneThotStyle) private var phoneThotStyle: ThotStyle = .never
|
||||
@AppStorage(Constants.extThotStyle) private var extThotStyle: ThotStyle = .never
|
||||
@AppStorage(Constants.thotGenderOption) private var thotGenderOption: String = "female"
|
||||
|
||||
var body: some View {
|
||||
if userStore.registeredUser?.NSFWValue ?? false {
|
||||
Group {
|
||||
Text("Phone THOT Style:")
|
||||
Picker("Phone THOT Style:", selection: $phoneThotStyle) {
|
||||
ForEach(ThotStyle.allCases, id: \.self) { style in
|
||||
Text(style.stringValue())
|
||||
.tag(phoneThotStyle.rawValue)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
|
||||
Divider()
|
||||
|
||||
Text("External THOT Style:")
|
||||
Picker("External THOT Style:", selection: $extThotStyle) {
|
||||
ForEach(ThotStyle.allCases, id: \.self) { style in
|
||||
Text(style.stringValue())
|
||||
.tag(extThotStyle.rawValue)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
|
||||
if let genderOptions = DataStore.shared.nsfwGenderOptions {
|
||||
Divider()
|
||||
Text("Video Gender:")
|
||||
Picker("Video Gender:", selection: $thotGenderOption) {
|
||||
ForEach(genderOptions, id: \.self) { option in
|
||||
Text(option.capitalized)
|
||||
.tag(option.lowercased())
|
||||
}
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ThotPreferenceView()
|
||||
}
|
||||
33
iphone/Werkout_ios/subview/TitleView.swift
Normal file
33
iphone/Werkout_ios/subview/TitleView.swift
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// TitleView.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 6/17/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct TitleView: View {
|
||||
@ObservedObject var bridgeModule = BridgeModule.shared
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
if let workout = bridgeModule.currentExerciseInfo.workout {
|
||||
Text(workout.name)
|
||||
.font(Font.system(size: 100))
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
if bridgeModule.currentWorkoutRunTimeInSeconds > -1 {
|
||||
Text("\(bridgeModule.currentWorkoutRunTimeInSeconds)")
|
||||
.font(Font.system(size: 100))
|
||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||
.padding(.trailing, 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
TitleView()
|
||||
}
|
||||
71
iphone/Werkout_ios/subview/WorkoutOverviewView.swift
Normal file
71
iphone/Werkout_ios/subview/WorkoutOverviewView.swift
Normal file
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// WorkoutOverviewView.swift
|
||||
// Werkout_ios
|
||||
//
|
||||
// Created by Trey Tartt on 7/9/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct WorkoutOverviewView: View {
|
||||
let workout: Workout
|
||||
var body: some View {
|
||||
VStack {
|
||||
HStack {
|
||||
VStack {
|
||||
Text(workout.name)
|
||||
.font(.title2)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
Text(workout.description ?? "")
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
if let estimatedTime = workout.estimatedTime {
|
||||
Text("Time: " + estimatedTime.asString(style: .abbreviated))
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
if let createdAt = workout.createdAt {
|
||||
Text(createdAt, style: .date)
|
||||
.font(.footnote)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
if let exerciseCount = workout.exercise_count {
|
||||
VStack {
|
||||
Text("\(exerciseCount)")
|
||||
.font(.body.bold())
|
||||
Text("exercises")
|
||||
.font(.footnote)
|
||||
.foregroundColor(Color(uiColor: .systemGray2))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let muscles = workout.muscles,
|
||||
muscles.joined(separator: ", ").count > 0{
|
||||
Divider()
|
||||
Text(muscles.joined(separator: ", "))
|
||||
.font(.footnote)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
if let equipment = workout.equipment,
|
||||
equipment.joined(separator: ", ").count > 0 {
|
||||
Divider()
|
||||
Text(equipment.joined(separator: ", "))
|
||||
.font(.footnote)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(Color(uiColor: .secondarySystemBackground))
|
||||
.cornerRadius(15)
|
||||
}
|
||||
}
|
||||
|
||||
struct WorkoutOverviewView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
WorkoutOverviewView(workout: PreviewData.allWorkouts()[2])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user