Merge develop -> main: full session (KC-51 through KC-71) #28
@@ -2250,3 +2250,45 @@ non-deprecated overload: `activity.end(nil, dismissalPolicy: .immediate)`.
|
|||||||
|
|
||||||
Files: `KisaniCal/Managers/LiveActivityManager.swift`,
|
Files: `KisaniCal/Managers/LiveActivityManager.swift`,
|
||||||
`KisaniCalWidgets/StopCountdownIntent.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 30–365 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.
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ import WidgetKit
|
|||||||
struct TaskItem: Identifiable, Codable, Equatable {
|
struct TaskItem: Identifiable, Codable, Equatable {
|
||||||
var id = UUID()
|
var id = UUID()
|
||||||
var title: String
|
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 dueDate: Date?
|
||||||
var hasTime: Bool = false
|
var hasTime: Bool = false
|
||||||
var isComplete: Bool = false
|
var isComplete: Bool = false
|
||||||
|
|||||||
Reference in New Issue
Block a user