End the watch session on Discard, plus start-flow UX tweaks
Watch-side follow-through for the End Workout flow: - The phone now pushes an authoritative set (in-progress, not-started, and completed within 24h) instead of the 25 most-recent workouts, and the watch prunes any workout absent from it. So a Discard/Delete (or a completed run aging out) drops off the watch, empties its active list, and ends the HKWorkoutSession — fixing the persistent wrist-raise re-foregrounding. The watch never originates a workout, so pruning can't lose local data; the 24h grace keeps a just-finished run on screen. The gate pops if the run you're viewing is pruned. UX tweaks: - The in-workout ⋯ is now a pull-down Menu (Add Exercise / End Workout) rather than an action sheet. - Starting a split while another workout is still active now prompts to end the current one(s) — keeping their progress — or run in parallel. Wired into both start paths (the split picker and "Start This Split"), via a shared WorkoutDocument.endKeepingProgress() helper. Claude-Session: https://claude.ai/code/session_01SCv7zvGFcKy47KSTnTLxRe
This commit is contained in:
@@ -60,9 +60,30 @@ final class PhoneConnectivityBridge: NSObject {
|
||||
session.isWatchAppInstalled else { return }
|
||||
|
||||
let splits = (try? context.fetch(FetchDescriptor<Split>(sortBy: [SortDescriptor(\.order)]))) ?? []
|
||||
var wDesc = FetchDescriptor<Workout>(sortBy: [SortDescriptor(\.start, order: .reverse)])
|
||||
wDesc.fetchLimit = 25
|
||||
let workouts = (try? context.fetch(wDesc)) ?? []
|
||||
|
||||
// The watch only needs what it can act on: every active run (in-progress /
|
||||
// not-started) plus recently-completed ones, kept ~24h so a run that just
|
||||
// finished still renders before the watch prunes it. The watch treats this as an
|
||||
// authoritative set and prunes anything absent — that's what ends its session on a
|
||||
// Discard/Delete. Active runs are sent in full (no cap): there are only ever a
|
||||
// handful, so "absent" unambiguously means "no longer active".
|
||||
let inProgressRaw = WorkoutStatus.inProgress.rawValue
|
||||
let notStartedRaw = WorkoutStatus.notStarted.rawValue
|
||||
let completedRaw = WorkoutStatus.completed.rawValue
|
||||
let cutoff = Date(timeIntervalSinceNow: -86_400)
|
||||
|
||||
let activeDesc = FetchDescriptor<Workout>(
|
||||
predicate: #Predicate<Workout> { $0.statusRaw == inProgressRaw || $0.statusRaw == notStartedRaw },
|
||||
sortBy: [SortDescriptor(\.start, order: .reverse)]
|
||||
)
|
||||
var completedDesc = FetchDescriptor<Workout>(
|
||||
predicate: #Predicate<Workout> { $0.statusRaw == completedRaw },
|
||||
sortBy: [SortDescriptor(\.start, order: .reverse)]
|
||||
)
|
||||
completedDesc.fetchLimit = 25
|
||||
let active = (try? context.fetch(activeDesc)) ?? []
|
||||
let recentCompleted = ((try? context.fetch(completedDesc)) ?? []).filter { ($0.end ?? $0.start) > cutoff }
|
||||
let workouts = active + recentCompleted
|
||||
|
||||
let restSeconds = UserDefaults.standard.object(forKey: WCPayload.restSecondsKey) as? Int ?? 45
|
||||
let doneCountdownSeconds = UserDefaults.standard.object(forKey: WCPayload.doneCountdownSecondsKey) as? Int ?? 5
|
||||
|
||||
Reference in New Issue
Block a user