WIP
This commit is contained in:
@@ -8,36 +8,27 @@
|
||||
import SwiftUI
|
||||
|
||||
struct AllWorkoutsListView: View {
|
||||
@State var searchString: String = ""
|
||||
let workouts: [Workout]
|
||||
|
||||
let selectedWorkout: ((Workout) -> Void)
|
||||
|
||||
var filteredWorkouts: [Workout] {
|
||||
if !searchString.isEmpty, searchString.count > 0 {
|
||||
return workouts.filter({
|
||||
if $0.name.lowercased().contains(searchString.lowercased()) {
|
||||
return true
|
||||
}
|
||||
if let equipment = $0.equipment?.joined(separator: "").lowercased(),
|
||||
equipment.contains(searchString.lowercased()) {
|
||||
return true
|
||||
}
|
||||
if let muscles = $0.muscles?.joined(separator: "").lowercased(),
|
||||
muscles.contains(searchString.lowercased()) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
} else {
|
||||
return workouts
|
||||
}
|
||||
enum SortType: String, CaseIterable {
|
||||
case name = "Name"
|
||||
case date = "Date"
|
||||
}
|
||||
|
||||
@State var searchString: String = ""
|
||||
@Binding var uniqueWorkoutUsers: [RegisteredUser]?
|
||||
@State private var filteredRegisterdUser: RegisteredUser?
|
||||
|
||||
let workouts: [Workout]
|
||||
let selectedWorkout: ((Workout) -> Void)
|
||||
@State var filteredWorkouts = [Workout]()
|
||||
var refresh: (() -> Void)
|
||||
@State var currentSort: SortType?
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if let filteredRegisterdUser = filteredRegisterdUser {
|
||||
Text((filteredRegisterdUser.firstName ?? "NA") + "'s Workouts")
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
LazyVStack(spacing: 20) {
|
||||
ForEach(filteredWorkouts, id:\.id) { workout in
|
||||
@@ -51,24 +42,120 @@ struct AllWorkoutsListView: View {
|
||||
}
|
||||
}
|
||||
.refreshable {
|
||||
refresh()
|
||||
refresh()
|
||||
}
|
||||
|
||||
TextField("Filter" ,text: $searchString)
|
||||
.padding()
|
||||
.textFieldStyle(OvalTextFieldStyle())
|
||||
HStack {
|
||||
TextField("Filter" ,text: $searchString)
|
||||
.padding()
|
||||
.textFieldStyle(OvalTextFieldStyle())
|
||||
|
||||
if let uniqueWorkoutUsers = uniqueWorkoutUsers {
|
||||
Menu(content: {
|
||||
ForEach(uniqueWorkoutUsers, id: \.self) { index in
|
||||
Button(action: {
|
||||
filteredRegisterdUser = index
|
||||
filteredWorkouts = filterWorkouts()
|
||||
}, label: {
|
||||
Text((index.firstName ?? "") + " -" + (index.lastName ?? ""))
|
||||
})
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
filteredRegisterdUser = nil
|
||||
filteredWorkouts = filterWorkouts()
|
||||
}, label: {
|
||||
Text("All")
|
||||
})
|
||||
|
||||
}, label: {
|
||||
Image(systemName: filteredRegisterdUser == nil ? "person.2" : "person.2.fill")
|
||||
.padding(.trailing)
|
||||
})
|
||||
}
|
||||
|
||||
Menu(content: {
|
||||
ForEach(SortType.allCases, id: \.self) { index in
|
||||
Button(action: {
|
||||
sortWorkouts(sortType: index)
|
||||
}, label: {
|
||||
Text(index.rawValue)
|
||||
})
|
||||
}
|
||||
}, label: {
|
||||
Image(systemName: "list.number")
|
||||
.padding(.trailing)
|
||||
})
|
||||
}
|
||||
}
|
||||
.onChange(of: searchString) { newValue in
|
||||
filteredWorkouts = filterWorkouts()
|
||||
}
|
||||
.onAppear{
|
||||
filteredWorkouts = filterWorkouts()
|
||||
}
|
||||
}
|
||||
|
||||
func sortWorkouts(sortType: SortType) {
|
||||
if currentSort == sortType {
|
||||
filteredWorkouts = filteredWorkouts.reversed()
|
||||
return
|
||||
}
|
||||
switch sortType {
|
||||
case .name:
|
||||
filteredWorkouts = filteredWorkouts.sorted(by: {
|
||||
$0.name < $1.name
|
||||
})
|
||||
case .date:
|
||||
filteredWorkouts = filteredWorkouts.sorted(by: {
|
||||
$0.createdAt ?? Date() < $1.createdAt ?? Date()
|
||||
})
|
||||
}
|
||||
currentSort = sortType
|
||||
}
|
||||
|
||||
func filterWorkouts() -> [Workout] {
|
||||
var matchingWorkouts = [Workout]()
|
||||
|
||||
if (!searchString.isEmpty && searchString.count > 0) {
|
||||
matchingWorkouts = workouts.filter({
|
||||
if $0.name.lowercased().contains(searchString.lowercased()) {
|
||||
return true
|
||||
}
|
||||
|
||||
if let equipment = $0.equipment?.joined(separator: "").lowercased(),
|
||||
equipment.contains(searchString.lowercased()) {
|
||||
return true
|
||||
}
|
||||
|
||||
if let muscles = $0.muscles?.joined(separator: "").lowercased(),
|
||||
muscles.contains(searchString.lowercased()) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
if matchingWorkouts.isEmpty {
|
||||
matchingWorkouts.append(contentsOf: workouts)
|
||||
}
|
||||
|
||||
if let filteredRegisterdUser = filteredRegisterdUser {
|
||||
matchingWorkouts = matchingWorkouts.filter({
|
||||
$0.registeredUser == filteredRegisterdUser
|
||||
})
|
||||
}
|
||||
|
||||
return matchingWorkouts
|
||||
}
|
||||
}
|
||||
|
||||
struct AllWorkoutsListView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
AllWorkoutsListView(workouts: PreviewData.allWorkouts(),
|
||||
selectedWorkout: { workout in
|
||||
|
||||
},
|
||||
refresh: {
|
||||
|
||||
})
|
||||
AllWorkoutsListView(uniqueWorkoutUsers: .constant([]),
|
||||
workouts: PreviewData.allWorkouts(),
|
||||
selectedWorkout: { workout in },
|
||||
refresh: { })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,10 @@ enum MainViewTypes: Int, CaseIterable {
|
||||
}
|
||||
|
||||
struct AllWorkoutsView: View {
|
||||
|
||||
@State var isUpdating = false
|
||||
@State var workouts: [Workout]?
|
||||
@State var uniqueWorkoutUsers: [RegisteredUser]?
|
||||
|
||||
var bridgeModule = BridgeModule.shared
|
||||
@State public var needsUpdating: Bool = true
|
||||
|
||||
@@ -61,6 +62,7 @@ struct AllWorkoutsView: View {
|
||||
selectedWorkout = bridgeModule.currentExerciseInfo.workout
|
||||
})
|
||||
|
||||
|
||||
switch selectedSegment {
|
||||
case .AllWorkout:
|
||||
if isUpdating {
|
||||
@@ -68,7 +70,9 @@ struct AllWorkoutsView: View {
|
||||
.progressViewStyle(.circular)
|
||||
}
|
||||
|
||||
AllWorkoutsListView(workouts: workouts, selectedWorkout: { workout in
|
||||
AllWorkoutsListView(uniqueWorkoutUsers: $uniqueWorkoutUsers,
|
||||
workouts: workouts,
|
||||
selectedWorkout: { workout in
|
||||
selectedWorkout = workout
|
||||
}, refresh: {
|
||||
self.needsUpdating = true
|
||||
@@ -155,21 +159,19 @@ struct AllWorkoutsView: View {
|
||||
|
||||
if needsUpdating {
|
||||
self.isUpdating = true
|
||||
dataStore.fetchAllData()
|
||||
|
||||
AllWorkoutFetchable().fetch(completion: { result in
|
||||
needsUpdating = false
|
||||
switch result {
|
||||
case .success(let model):
|
||||
DispatchQueue.main.async {
|
||||
self.workouts = model
|
||||
self.isUpdating = false
|
||||
}
|
||||
case .failure(_):
|
||||
DispatchQueue.main.async {
|
||||
self.isUpdating = false
|
||||
dataStore.fetchAllData(completion: {
|
||||
DispatchQueue.main.async {
|
||||
guard let allWorkouts = dataStore.allWorkouts else {
|
||||
return
|
||||
}
|
||||
self.workouts = allWorkouts.sorted(by: {
|
||||
$0.createdAt ?? Date() < $1.createdAt ?? Date()
|
||||
})
|
||||
self.isUpdating = false
|
||||
self.uniqueWorkoutUsers = dataStore.workoutsUniqueUsers
|
||||
}
|
||||
|
||||
self.isUpdating = false
|
||||
})
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -19,6 +19,17 @@ struct WorkoutOverviewView: View {
|
||||
|
||||
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 {
|
||||
|
||||
@@ -17,8 +17,7 @@ struct ExternalWorkoutDetailView: View {
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
if let workout = bridgeModule.currentExerciseInfo.workout,
|
||||
let exercise = bridgeModule.currentExerciseInfo.currentExercise {
|
||||
if let workout = bridgeModule.currentExerciseInfo.workout {
|
||||
GeometryReader { metrics in
|
||||
VStack {
|
||||
HStack {
|
||||
@@ -32,7 +31,7 @@ struct ExternalWorkoutDetailView: View {
|
||||
|
||||
VStack {
|
||||
ExtExerciseList(workout: workout,
|
||||
currentExercise: exercise)
|
||||
allSupersetExecerciseIndex: bridgeModule.currentExerciseInfo.allSupersetExecerciseIndex)
|
||||
|
||||
if let currentExercisePositionString = bridgeModule.currentExercisePositionString {
|
||||
Text(currentExercisePositionString)
|
||||
@@ -153,63 +152,64 @@ struct TitleView: View {
|
||||
|
||||
struct ExtExerciseList: View {
|
||||
var workout: Workout
|
||||
var currentExercise: SupersetExercise
|
||||
var allSupersetExecerciseIndex: Int
|
||||
|
||||
var body: some View {
|
||||
if let supersets = workout.supersets {
|
||||
ScrollViewReader { proxy in
|
||||
List() {
|
||||
ForEach(supersets.indices, id: \.self) { supersetIndex in
|
||||
let superset = supersets[supersetIndex]
|
||||
|
||||
Section(content: {
|
||||
ForEach(superset.exercises.indices, id: \.self) { exerciseIndex in
|
||||
let supersetExecercise = superset.exercises[exerciseIndex]
|
||||
|
||||
HStack {
|
||||
if supersetExecercise.id == currentExercise.id {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.green)
|
||||
.font(Font.system(size: 55))
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(1)
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
|
||||
Text(supersetExecercise.exercise.name)
|
||||
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: "checkmark")
|
||||
.foregroundColor(.green)
|
||||
.font(Font.system(size: 55))
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(3)
|
||||
.padding()
|
||||
|
||||
Spacer()
|
||||
.lineLimit(1)
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
.id(supersetExecercise.id)
|
||||
}
|
||||
}, header: {
|
||||
HStack {
|
||||
Text(superset.name ?? "--")
|
||||
|
||||
Text(supersetExecercise.exercise.name)
|
||||
.font(Font.system(size: 55))
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(3)
|
||||
.padding()
|
||||
|
||||
Spacer()
|
||||
Text("\(superset.rounds) rounds")
|
||||
.font(Font.system(size: 55))
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(3)
|
||||
.padding()
|
||||
}
|
||||
})
|
||||
.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()
|
||||
}
|
||||
.onChange(of: currentExercise, perform: { newValue in
|
||||
withAnimation {
|
||||
print(newValue.id)
|
||||
proxy.scrollTo(newValue.id, anchor: .top)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,12 @@ struct ExerciseListView: View {
|
||||
Text("\(superset.rounds) rounds")
|
||||
.foregroundColor(Color("appColor"))
|
||||
.bold()
|
||||
|
||||
if let estimatedTime = superset.estimatedTime {
|
||||
Text("@ " + estimatedTime.asString(style: .abbreviated))
|
||||
.foregroundColor(Color("appColor"))
|
||||
.bold()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -18,12 +18,20 @@ struct InfoView: View {
|
||||
.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])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ struct WorkoutDetailView: View {
|
||||
if phoneThotStyle != .off {
|
||||
GeometryReader { metrics in
|
||||
ZStack {
|
||||
|
||||
PlayerView(player: $avPlayer)
|
||||
.frame(width: metrics.size.width * 1, height: metrics.size.height * 1)
|
||||
.onAppear{
|
||||
|
||||
Reference in New Issue
Block a user