feat: home/lock widgets, Health-aware streaks, workout editor, and entitlement fixes
Widgets (new KisaniCalWidgets extension) - Event Countdown widget: configurable via AppIntent, pick from your events or set a custom name/date; dot-grid progress toward the event. - Day Progress and Tasks-Done-Today widgets; lock screen widgets. - Shared dot grid sizes circles to fill the widget evenly at any count. - Tasks/calendar prefs now persist to the App Group so widgets read live data. Health & streaks - Persist HealthKit authorization and re-establish on launch (fixes the Today dashboard and streak sync disappearing after relaunch). - Add a Health permission toggle to onboarding; unify Settings/Profile on the manager's state. - Day streak counts from a logged Health workout OR marking all exercises complete; nudge to mark exercises complete even when Health logged the workout. Workout editor - Drag to reorder exercises and move them across sections (drag-and-drop); swipe to delete. - Add-exercise sheet: global search and keep-adding-multiple with a running count. Fixes - Restore app entitlements link (Sign in with Apple, HealthKit, App Group, iCloud). - Add HealthKit usage strings; widget Info.plist NSExtension + nested bundle id. - Calendar "Connect" routes to Settings when access was denied. - Bake DEVELOPMENT_TEAM and shared versions into project.yml. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import SwiftUI
|
||||
import WidgetKit
|
||||
|
||||
struct ContentView: View {
|
||||
@StateObject private var taskVM = TaskViewModel()
|
||||
@@ -86,7 +87,13 @@ struct ContentView: View {
|
||||
if !hasOnboarded { showOnboarding = true }
|
||||
CloudSyncManager.shared.restoreSettings()
|
||||
CloudSyncManager.shared.backupSettings()
|
||||
HealthKitManager.shared.bootstrap()
|
||||
NotificationManager.shared.reschedule(workoutVM: workoutVM, taskVM: taskVM)
|
||||
// Pull workouts from Health, then reschedule so a detected workout updates reminders.
|
||||
Task {
|
||||
await workoutVM.syncFromHealthKit()
|
||||
NotificationManager.shared.reschedule(workoutVM: workoutVM, taskVM: taskVM)
|
||||
}
|
||||
}
|
||||
.onChange(of: scenePhase) { phase in
|
||||
if phase == .active {
|
||||
@@ -94,11 +101,15 @@ struct ContentView: View {
|
||||
workoutVM.checkPendingHealthConfirm()
|
||||
CloudSyncManager.shared.backupSettings()
|
||||
NotificationManager.shared.reschedule(workoutVM: workoutVM, taskVM: taskVM)
|
||||
Task { await workoutVM.syncFromHealthKit() }
|
||||
Task {
|
||||
await workoutVM.syncFromHealthKit()
|
||||
NotificationManager.shared.reschedule(workoutVM: workoutVM, taskVM: taskVM)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: taskVM.tasks) { _ in
|
||||
NotificationManager.shared.reschedule(workoutVM: workoutVM, taskVM: taskVM)
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
}
|
||||
.onChange(of: workoutVM.doneSets) { _ in
|
||||
NotificationManager.shared.reschedule(workoutVM: workoutVM, taskVM: taskVM)
|
||||
|
||||
Reference in New Issue
Block a user