Merge develop -> main: full session (KC-51 through KC-71) #28

Merged
kutesir merged 64 commits from develop into main 2026-07-12 22:57:14 +00:00
2 changed files with 46 additions and 0 deletions
Showing only changes of commit b1b33b6014 - Show all commits

View File

@@ -2250,3 +2250,45 @@ non-deprecated overload: `activity.end(nil, dismissalPolicy: .immediate)`.
Files: `KisaniCal/Managers/LiveActivityManager.swift`,
`KisaniCalWidgets/StopCountdownIntent.swift`.
## KC-65 — TaskItem never actually stored a creation date
Status: Implemented (not build-verified — no iOS runtime here)
Reported by: User, from a Track Countdown lock-screen widget stuck at 0%
Area: Model / widgets (Track Countdown progress dots)
### Description
The countdown-dots widget (`KisaniCalWidgets.swift`'s `eventStartDate`) was
already written to prefer `task.createdAt` as the progress bar's start
date — but `TaskItem` never had a `createdAt` property at all, so it always
decoded to `nil` and every task fell through to a fallback chain: a
per-task "first render" anchor persisted in `UserDefaults.kisani`
(`kisani.widget.progressDots.anchor.<id>`), or failing that, an inferred
legacy start (`remainingDays * 2`, capped 30365 days). That anchor can
reset to "now" under several conditions (widget re-added, storage cleared,
the `looksLikeUpgradeAnchor` heuristic firing) — which is exactly the
"starting from scratch on every update" behavior the user was seeing.
### Change
Added `var createdAt: Date? = Date()` to `TaskItem` (`Models/TaskItem.swift`)
— optional so existing saved tasks decode safely (missing key → `nil`,
same pattern as `recurrenceEnd`), but the `Date()` default is evaluated
fresh at each construction site, so every newly created task now genuinely
records its creation moment. No call sites needed updating: `addTask` uses
the compiler-synthesized memberwise init and doesn't pass `createdAt`
explicitly, so it gets the live default; `occurrenceCopy` copies the
struct (`var c = t`), so recurring-task instances correctly inherit the
series' original `createdAt` rather than getting a fresh one.
The widget-side plumbing (`WidgetData.swift`'s `RawTask`/`WidgetTask`
decode, `eventStartDate`'s preference order) already existed and needed no
change — it was just never being fed a real value.
Files: `KisaniCal/Models/TaskItem.swift`.
### Note
Not build-verified (no iOS runtime here). Self-reviewed: balanced braces/
parens (one pre-existing stray `(` predates this change, confirmed earlier
in KC-62). Existing tasks (created before this fix) still have no real
creation date and will keep using the anchor/inferred fallback — there's
no way to retroactively know when they were actually created.

View File

@@ -5,6 +5,10 @@ import WidgetKit
struct TaskItem: Identifiable, Codable, Equatable {
var id = UUID()
var title: String
// Optional so existing saved tasks decode safely (missing key nil); the
// `Date()` default only fires for genuinely new tasks constructed after
// this field existed it's evaluated fresh at each construction site.
var createdAt: Date? = Date()
var dueDate: Date?
var hasTime: Bool = false
var isComplete: Bool = false