Workout: auto-switch to today's program + Rest Day state
The Workout tab kept showing the last-used program (with its old completion) regardless of the weekly schedule, so a rest day still displayed a stale, fully-checked workout. - On a new day, the active program now follows the weekly schedule (applyScheduledProgramForToday in resetSetsForNewDayIfNeeded). - Add isRestDayToday + a Rest Day card shown when today has no scheduled program, with a "Work out anyway" override. Header reads "Rest Day". - Daily set reset (KC-1) unchanged — no day starts pre-checked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -80,7 +80,7 @@
|
|||||||
0506183945D16EC443A69651 /* LiveActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivityManager.swift; sourceTree = "<group>"; };
|
0506183945D16EC443A69651 /* LiveActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivityManager.swift; sourceTree = "<group>"; };
|
||||||
0513EB8C7F6B6853B9E93E09 /* TaskLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskLiveActivity.swift; sourceTree = "<group>"; };
|
0513EB8C7F6B6853B9E93E09 /* TaskLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskLiveActivity.swift; sourceTree = "<group>"; };
|
||||||
0C96EC00F6B021DBA3CC1A79 /* ProfileSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileSetupView.swift; sourceTree = "<group>"; };
|
0C96EC00F6B021DBA3CC1A79 /* ProfileSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileSetupView.swift; sourceTree = "<group>"; };
|
||||||
0D6D6740721F01A74E404EB9 /* KisaniCalWidgets.appex */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "wrapper.app-extension"; path = KisaniCalWidgets.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
0D6D6740721F01A74E404EB9 /* KisaniCalWidgets.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = KisaniCalWidgets.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
106EEF572C6F8990408329F0 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
|
106EEF572C6F8990408329F0 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
|
||||||
1AA7498EFED7692022F3E7E1 /* KisaniCal.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = KisaniCal.entitlements; sourceTree = "<group>"; };
|
1AA7498EFED7692022F3E7E1 /* KisaniCal.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = KisaniCal.entitlements; sourceTree = "<group>"; };
|
||||||
208F82348DEBD9FF7B0DCF17 /* MyTasksWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyTasksWidget.swift; sourceTree = "<group>"; };
|
208F82348DEBD9FF7B0DCF17 /* MyTasksWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyTasksWidget.swift; sourceTree = "<group>"; };
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
9100804DB1E61EA882CC54DA /* FloatingTabState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingTabState.swift; sourceTree = "<group>"; };
|
9100804DB1E61EA882CC54DA /* FloatingTabState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingTabState.swift; sourceTree = "<group>"; };
|
||||||
92824ED40ECD41EFD4F78BEC /* ISSUES.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = ISSUES.md; sourceTree = "<group>"; };
|
92824ED40ECD41EFD4F78BEC /* ISSUES.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = ISSUES.md; sourceTree = "<group>"; };
|
||||||
93D045FE3DEB1D22D908A29F /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
|
93D045FE3DEB1D22D908A29F /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
|
||||||
9BD1F84B4F45B12CB9B9F1B2 /* KisaniCal.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = KisaniCal.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
9BD1F84B4F45B12CB9B9F1B2 /* KisaniCal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KisaniCal.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
ADF6CCD95A587E26E30F5712 /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = "<group>"; };
|
ADF6CCD95A587E26E30F5712 /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = "<group>"; };
|
||||||
AF905C574F34B4EE51A8D21E /* AppGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppGroup.swift; sourceTree = "<group>"; };
|
AF905C574F34B4EE51A8D21E /* AppGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppGroup.swift; sourceTree = "<group>"; };
|
||||||
BC5E179A9189B0A8C3F856F6 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
BC5E179A9189B0A8C3F856F6 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||||
@@ -289,7 +289,7 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
BuildIndependentTargetsInParallel = YES;
|
BuildIndependentTargetsInParallel = YES;
|
||||||
LastUpgradeCheck = 1500;
|
LastUpgradeCheck = 2620;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
28B43516AD88946E21D9BFE0 = {
|
28B43516AD88946E21D9BFE0 = {
|
||||||
DevelopmentTeam = K8BLMMR883;
|
DevelopmentTeam = K8BLMMR883;
|
||||||
@@ -423,6 +423,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
|
MARKETING_VERSION = 2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal;
|
PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_VERSION = 5.9;
|
SWIFT_VERSION = 5.9;
|
||||||
@@ -469,6 +470,7 @@
|
|||||||
DEVELOPMENT_TEAM = K8BLMMR883;
|
DEVELOPMENT_TEAM = K8BLMMR883;
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
@@ -483,6 +485,7 @@
|
|||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
@@ -498,7 +501,12 @@
|
|||||||
GENERATE_INFOPLIST_FILE = NO;
|
GENERATE_INFOPLIST_FILE = NO;
|
||||||
INFOPLIST_FILE = KisaniCalWidgets/Info.plist;
|
INFOPLIST_FILE = KisaniCalWidgets/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal.KisaniCalWidgets;
|
PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal.KisaniCalWidgets;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_VERSION = 5.9;
|
SWIFT_VERSION = 5.9;
|
||||||
@@ -545,6 +553,7 @@
|
|||||||
DEVELOPMENT_TEAM = K8BLMMR883;
|
DEVELOPMENT_TEAM = K8BLMMR883;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
ENABLE_TESTABILITY = YES;
|
ENABLE_TESTABILITY = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
@@ -566,6 +575,7 @@
|
|||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
@@ -581,7 +591,12 @@
|
|||||||
GENERATE_INFOPLIST_FILE = NO;
|
GENERATE_INFOPLIST_FILE = NO;
|
||||||
INFOPLIST_FILE = KisaniCalWidgets/Info.plist;
|
INFOPLIST_FILE = KisaniCalWidgets/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal.KisaniCalWidgets;
|
PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal.KisaniCalWidgets;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_VERSION = 5.9;
|
SWIFT_VERSION = 5.9;
|
||||||
@@ -615,6 +630,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
|
MARKETING_VERSION = 2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal;
|
PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_VERSION = 5.9;
|
SWIFT_VERSION = 5.9;
|
||||||
|
|||||||
Binary file not shown.
@@ -1,11 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1500"
|
LastUpgradeVersion = "2620"
|
||||||
version = "1.7">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
buildImplicitDependencies = "YES"
|
buildImplicitDependencies = "YES">
|
||||||
runPostActionsOnFailure = "NO">
|
|
||||||
<BuildActionEntries>
|
<BuildActionEntries>
|
||||||
<BuildActionEntry
|
<BuildActionEntry
|
||||||
buildForTesting = "YES"
|
buildForTesting = "YES"
|
||||||
@@ -27,8 +26,7 @@
|
|||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
onlyGenerateCoverageForSpecifiedTargets = "NO">
|
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
@@ -40,8 +38,6 @@
|
|||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<Testables>
|
<Testables>
|
||||||
</Testables>
|
</Testables>
|
||||||
<CommandLineArguments>
|
|
||||||
</CommandLineArguments>
|
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
@@ -63,8 +59,6 @@
|
|||||||
ReferencedContainer = "container:KisaniCal.xcodeproj">
|
ReferencedContainer = "container:KisaniCal.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
<CommandLineArguments>
|
|
||||||
</CommandLineArguments>
|
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
@@ -82,8 +76,6 @@
|
|||||||
ReferencedContainer = "container:KisaniCal.xcodeproj">
|
ReferencedContainer = "container:KisaniCal.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
<CommandLineArguments>
|
|
||||||
</CommandLineArguments>
|
|
||||||
</ProfileAction>
|
</ProfileAction>
|
||||||
<AnalyzeAction
|
<AnalyzeAction
|
||||||
buildConfiguration = "Debug">
|
buildConfiguration = "Debug">
|
||||||
|
|||||||
@@ -313,10 +313,32 @@ class WorkoutViewModel: ObservableObject {
|
|||||||
let today = iso.string(from: Date())
|
let today = iso.string(from: Date())
|
||||||
let last = UserDefaults.kisani.string(forKey: kLastActiveDay)
|
let last = UserDefaults.kisani.string(forKey: kLastActiveDay)
|
||||||
guard last != today else { return }
|
guard last != today else { return }
|
||||||
if last != nil { clearAllDoneSets() } // skip the wipe on the very first launch
|
if last != nil {
|
||||||
|
clearAllDoneSets() // skip the wipe on the very first launch
|
||||||
|
applyScheduledProgramForToday() // follow the weekly schedule on a new day
|
||||||
|
}
|
||||||
UserDefaults.kisani.set(today, forKey: kLastActiveDay)
|
UserDefaults.kisani.set(today, forKey: kLastActiveDay)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The program assigned to today in the weekly schedule (nil = rest day).
|
||||||
|
var todaysScheduledProgram: WorkoutProgram? { program(for: Date()) }
|
||||||
|
|
||||||
|
/// True when a schedule exists but today has no program — a rest day.
|
||||||
|
var isRestDayToday: Bool {
|
||||||
|
let weekday = Calendar.current.component(.weekday, from: Date())
|
||||||
|
return !schedule.isEmpty && schedule[weekday] == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/// On a new day, make the active program match the day's schedule so the
|
||||||
|
/// Workout tab shows today's workout instead of yesterday's. Rest days leave
|
||||||
|
/// the active program untouched (the view shows a rest state instead).
|
||||||
|
private func applyScheduledProgramForToday() {
|
||||||
|
guard let scheduled = todaysScheduledProgram, scheduled.id != activeProgramId else { return }
|
||||||
|
activeProgramId = scheduled.id
|
||||||
|
activeSections = scheduled.sections
|
||||||
|
save()
|
||||||
|
}
|
||||||
|
|
||||||
/// Unchecks every set across all programs and clears the in-progress session.
|
/// Unchecks every set across all programs and clears the in-progress session.
|
||||||
private func clearAllDoneSets() {
|
private func clearAllDoneSets() {
|
||||||
for pi in programs.indices {
|
for pi in programs.indices {
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ struct WorkoutView: View {
|
|||||||
@State private var showManage = false
|
@State private var showManage = false
|
||||||
@State private var showAddSection = false
|
@State private var showAddSection = false
|
||||||
@State private var isReordering = false
|
@State private var isReordering = false
|
||||||
|
@State private var trainAnyway = false // override the rest-day state for today
|
||||||
@ObservedObject private var tm = TutorialManager.shared
|
@ObservedObject private var tm = TutorialManager.shared
|
||||||
private let restTick = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
|
private let restTick = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
|
||||||
|
|
||||||
@@ -70,6 +71,10 @@ struct WorkoutView: View {
|
|||||||
return f.string(from: Date())
|
return f.string(from: Date())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Show the rest-day state when today has no scheduled program and the user
|
||||||
|
/// hasn't chosen to train anyway.
|
||||||
|
private var isRestState: Bool { vm.isRestDayToday && !trainAnyway }
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack(alignment: .bottomTrailing) {
|
ZStack(alignment: .bottomTrailing) {
|
||||||
if isReordering {
|
if isReordering {
|
||||||
@@ -85,8 +90,8 @@ struct WorkoutView: View {
|
|||||||
HStack {
|
HStack {
|
||||||
VStack(alignment: .leading, spacing: 2) {
|
VStack(alignment: .leading, spacing: 2) {
|
||||||
HStack(spacing: 6) {
|
HStack(spacing: 6) {
|
||||||
ProgramIcon(name: vm.workoutEmoji, size: 14, color: AppColors.accent)
|
ProgramIcon(name: isRestState ? "moon.zzz.fill" : vm.workoutEmoji, size: 14, color: AppColors.accent)
|
||||||
Text(vm.workoutTitle)
|
Text(isRestState ? "Rest Day" : vm.workoutTitle)
|
||||||
.font(AppFonts.sans(15, weight: .semibold))
|
.font(AppFonts.sans(15, weight: .semibold))
|
||||||
.foregroundColor(AppColors.text(cs))
|
.foregroundColor(AppColors.text(cs))
|
||||||
}
|
}
|
||||||
@@ -102,6 +107,15 @@ struct WorkoutView: View {
|
|||||||
.listRowInsets(EdgeInsets(top: 8, leading: 18, bottom: 4, trailing: 18))
|
.listRowInsets(EdgeInsets(top: 8, leading: 18, bottom: 4, trailing: 18))
|
||||||
.moveDisabled(true)
|
.moveDisabled(true)
|
||||||
|
|
||||||
|
if isRestState {
|
||||||
|
// ── Rest Day ──
|
||||||
|
RestDayCard(onTrain: { withAnimation(KisaniSpring.snappy) { trainAnyway = true } })
|
||||||
|
.listRowBackground(Color.clear)
|
||||||
|
.listRowSeparator(.hidden)
|
||||||
|
.listRowInsets(EdgeInsets(top: 16, leading: 14, bottom: 6, trailing: 14))
|
||||||
|
.moveDisabled(true)
|
||||||
|
} else {
|
||||||
|
|
||||||
// ── Dot Grid Progress ──
|
// ── Dot Grid Progress ──
|
||||||
DotProgressCard(progress: vm.progress, done: vm.doneSets, total: vm.totalSets)
|
DotProgressCard(progress: vm.progress, done: vm.doneSets, total: vm.totalSets)
|
||||||
.listRowBackground(Color.clear)
|
.listRowBackground(Color.clear)
|
||||||
@@ -200,6 +214,7 @@ struct WorkoutView: View {
|
|||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
.listRowInsets(EdgeInsets(top: 4, leading: 14, bottom: 100, trailing: 14))
|
.listRowInsets(EdgeInsets(top: 4, leading: 14, bottom: 100, trailing: 14))
|
||||||
.moveDisabled(true)
|
.moveDisabled(true)
|
||||||
|
} // end non-rest content
|
||||||
}
|
}
|
||||||
.listStyle(.plain)
|
.listStyle(.plain)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
@@ -537,6 +552,41 @@ struct DotProgressCard: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Rest Day Card
|
||||||
|
struct RestDayCard: View {
|
||||||
|
@Environment(\.colorScheme) private var cs
|
||||||
|
let onTrain: () -> Void
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 12) {
|
||||||
|
Image(systemName: "moon.zzz.fill")
|
||||||
|
.font(.system(size: 30, weight: .light))
|
||||||
|
.foregroundColor(AppColors.accent)
|
||||||
|
VStack(spacing: 4) {
|
||||||
|
Text("Rest Day")
|
||||||
|
.font(AppFonts.sans(17, weight: .bold))
|
||||||
|
.foregroundColor(AppColors.text(cs))
|
||||||
|
Text("No workout scheduled today. Recover and come back stronger.")
|
||||||
|
.font(AppFonts.sans(12))
|
||||||
|
.foregroundColor(AppColors.text3(cs))
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
}
|
||||||
|
Button(action: onTrain) {
|
||||||
|
Text("Work out anyway")
|
||||||
|
.font(AppFonts.sans(13, weight: .semibold))
|
||||||
|
.foregroundColor(AppColors.accent)
|
||||||
|
.padding(.horizontal, 16).padding(.vertical, 9)
|
||||||
|
.background(AppColors.accentSoft)
|
||||||
|
.clipShape(Capsule())
|
||||||
|
}
|
||||||
|
.buttonStyle(PressButtonStyle(scale: 0.96))
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.padding(.vertical, 28).padding(.horizontal, 16)
|
||||||
|
.cardStyle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Streak Banner
|
// MARK: - Streak Banner
|
||||||
struct StreakBannerView: View {
|
struct StreakBannerView: View {
|
||||||
@Environment(\.colorScheme) private var cs
|
@Environment(\.colorScheme) private var cs
|
||||||
|
|||||||
Reference in New Issue
Block a user