feat: HealthKit workout sync, confirmation notification, health stats on Today dashboard
- HealthKitManager: add fetchWorkoutDates(from:to:) to read past workouts from Apple Health - WorkoutViewModel: syncFromHealthKit() merges HealthKit workout dates into streak (runs on every foreground); checkPendingHealthConfirm() picks up confirmed workouts from notification action - NotificationManager: WORKOUT_CONFIRM category with 'Yes, log it' action; scheduleWorkoutConfirm() fires weekly on scheduled workout days at configurable time (default 8pm); LOG_WORKOUT action writes pendingConfirm to UserDefaults - SettingsView (Workout Settings): new HEALTH SYNC section with 'Sync now' button and 'Workout confirmation reminder' toggle + time picker - TodayView: TodayHealthStrip shows steps, active calories, resting HR, and workout streak in a 4-pill row below the date header (only shown when HealthKit is authorized); refreshes on appear and foreground - FAB: Matrix FAB padding fixed to match Today/Calendar position (.bottom 10) Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
This commit is contained in:
@@ -310,6 +310,35 @@ class WorkoutViewModel: ObservableObject {
|
||||
sessionStartDate = nil
|
||||
}
|
||||
|
||||
// Pull completed workout dates from Apple Health and merge into local streak
|
||||
@MainActor
|
||||
func syncFromHealthKit() async {
|
||||
guard HealthKitManager.shared.authorized else { return }
|
||||
guard let ninetyDaysAgo = Calendar.current.date(byAdding: .day, value: -90, to: Date()) else { return }
|
||||
let hkDates = await HealthKitManager.shared.fetchWorkoutDates(from: ninetyDaysAgo, to: Date())
|
||||
guard !hkDates.isEmpty else { return }
|
||||
var newSet = Set(workoutDates); var updated = false
|
||||
for d in hkDates where !newSet.contains(d) { newSet.insert(d); updated = true }
|
||||
if updated { workoutDates = Array(newSet); save() }
|
||||
}
|
||||
|
||||
// Called on foreground — picks up "Yes I did it" tapped in notification
|
||||
@MainActor
|
||||
func checkPendingHealthConfirm() {
|
||||
let key = "kisani.workout.pendingConfirm"
|
||||
guard let dateStr = UserDefaults.standard.string(forKey: key) else { return }
|
||||
UserDefaults.standard.removeObject(forKey: key)
|
||||
guard !workoutDates.contains(dateStr) else { return }
|
||||
workoutDates.append(dateStr)
|
||||
save()
|
||||
let fmt = DateFormatter(); fmt.dateFormat = "yyyy-MM-dd"
|
||||
if let date = fmt.date(from: dateStr) {
|
||||
let s = Calendar.current.startOfDay(for: date)
|
||||
let e = Calendar.current.date(byAdding: .hour, value: 1, to: s) ?? s
|
||||
Task { await HealthKitManager.shared.saveWorkout(start: s, end: e) }
|
||||
}
|
||||
}
|
||||
|
||||
func seedStreak(days: Int) {
|
||||
let cal = Calendar.current
|
||||
var newDates = Set(workoutDates)
|
||||
|
||||
Reference in New Issue
Block a user