feat: My Tasks widget, event countdown redesign, completed section

- My Tasks widget: today's tasks with interactive check-off (ToggleTaskIntent)
  and a "+" that opens the app's add-task sheet; writes preserve all task
  fields and sync via the App Group.
- Event Countdown: AZURE/AWS-style header (accent name + countdown label +
  percent); tap the label to cycle days → weeks → time remaining.
- Today: collapsible "Completed" archive section below Upcoming.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Robin Kutesa
2026-06-03 13:31:04 +03:00
parent 8b094691f5
commit 646cdf6a9b
10 changed files with 386 additions and 44 deletions

View File

@@ -68,6 +68,14 @@ struct ContentView: View {
private var isIPad: Bool { UIDevice.current.userInterfaceIdiom == .pad }
/// Picks up the "+" tapped on the My Tasks widget and opens the add-task sheet.
private func consumeWidgetAddTask() {
guard UserDefaults.kisani.bool(forKey: "kisani.widget.openAddTask") else { return }
UserDefaults.kisani.removeObject(forKey: "kisani.widget.openAddTask")
selectedTab = 0
showQuickAddTask = true
}
var body: some View {
Group {
if isIPad {
@@ -89,6 +97,7 @@ struct ContentView: View {
CloudSyncManager.shared.backupSettings()
HealthKitManager.shared.bootstrap()
NotificationManager.shared.reschedule(workoutVM: workoutVM, taskVM: taskVM)
consumeWidgetAddTask()
// Pull workouts from Health, then reschedule so a detected workout updates reminders.
Task {
await workoutVM.syncFromHealthKit()
@@ -98,6 +107,7 @@ struct ContentView: View {
.onChange(of: scenePhase) { phase in
if phase == .active {
taskVM.reload()
consumeWidgetAddTask()
workoutVM.checkPendingHealthConfirm()
CloudSyncManager.shared.backupSettings()
NotificationManager.shared.reschedule(workoutVM: workoutVM, taskVM: taskVM)