Tap body → marks complete + navigates to tasks.
Swipe action 'Mark Complete' → marks complete without opening app.
TaskViewModel.reload() called on foreground to sync UserDefaults changes
made while app was backgrounded.
Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
Evening check-in no longer embeds the urgent task count in the body —
repeating notifications bake that value at schedule time so it always
shows a stale number. Replaced with a generic prompt.
didReceive now routes notification taps via ShortcutHandler:
'workout' deeplink → Workout tab, everything else → Today tab.
Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
updateBadge() counts all incomplete tasks with a due date earlier
than tomorrow (i.e. overdue or due today) and calls setBadgeCount().
Called inside reschedule() so the badge updates on foreground, task
changes, and workout set completions — same triggers as notifications.
Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
- TutorialManager: tracks 4 steps (NLP, Calendar, Matrix, Workout)
persisted via AppStorage; shown once then never again
- TutorialView: fullscreen overlay with even-odd spotlight cutout,
animated accent border, ultraThinMaterial callout card, pill progress
dots, Next/Get started button, and Skip
- TutorialFrameKey: PreferenceKey collects CGRect for each anchor index;
.tutorialAnchor() modifier wired to FAB (step 0) and all 4 tab buttons
- Callout auto-positions above or below the spotlight based on screen half
- ContentView collects frames via onPreferenceChange and layers the overlay
Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
Needed because self is captured in @Sendable closures passed to
UNUserNotificationCenter callbacks. Safe: the only mutable state
(isAuthorized) is exclusively written on the main thread.
Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
- Remove @MainActor from class (caused all Sendable/isolation cascade)
- Keep @MainActor only on reschedule() where VM access is needed
- Snapshot tasks in reschedule() so scheduleTasks() never captures
a non-Sendable TaskViewModel across async boundaries
- Capture center locally in scheduleTasks() to avoid @MainActor
property access inside the @Sendable getPendingNotificationRequests callback
- Mark UNUserNotificationCenterDelegate methods nonisolated to satisfy
protocol conformance without actor crossing
Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
Snapshot WorkoutViewModel's schedule/programs/progress before the
async getNotificationSettings callback so scheduleWorkout receives
plain value types instead of MainActor-isolated properties.
Also fix unused-mutation warning: email in handleApple changed to let.
Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
- Remove all personal sampleTasks from TaskViewModel (tasks start empty)
- Namespace UserDefaults keys by Apple ID for task and workout data isolation
- Add ProfileSetupView for name capture after first Sign in with Apple
- Add updateDisplayName() to AuthManager
- Route to ProfileSetupView when user has no display name yet
Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
Adds four UIApplicationShortcutItems registered dynamically at launch:
• Add Task — opens the add-task sheet immediately, any tab
• Today — switches to the Today tab
• Calendar — switches to the Calendar tab
• Workout — switches to the Workout tab (only registered if the
workout tab is enabled in settings; re-registers when the setting changes)
Cold-launch and foreground-activation paths both handled via AppDelegate.
ShortcutHandler singleton bridges UIKit callbacks to SwiftUI via @Published.
Tasks now survive app restarts via UserDefaults JSON. Each task with a
due time fires a notification at that exact time; date-only tasks fire
at 9am on the due date. Completing or deleting a task cancels its
pending notification. ContentView now reschedules on any task change.