liveactivity+widgets: fix iOS 16.2 build error, extend createdAt to Bars widget (KC-66)
Some checks failed
CI / build-and-test (push) Has been cancelled

end(_:dismissalPolicy:) requires iOS 16.2 but LiveActivityManager.end
only gated to 16.1 (Live Activities' own minimum) - branch on
#available with the deprecated overload as fallback. Separately,
auditing all countdown widgets per user request found the "Event
Countdown (Bars)" widget family had its own independent start-date
resolution that never considered the task's real creation date at
all - EventEntity now carries createdAt and resolveSpan prefers it
the same way KC-65 already does for the dot-grid widget.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
kutesir
2026-07-09 22:52:12 +03:00
parent b1b33b6014
commit 3512d55503
3 changed files with 79 additions and 8 deletions

View File

@@ -78,6 +78,7 @@ struct EventEntity: AppEntity {
let dueDate: Date?
var isRecurring: Bool = false
var recurrenceLabel: String? = nil
var createdAt: Date? = nil
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Event"
static var defaultQuery = EventQuery()
@@ -105,7 +106,8 @@ struct EventQuery: EntityQuery {
.filter { !$0.isComplete && $0.dueDate != nil }
.sorted { ($0.dueDate ?? .distantFuture) < ($1.dueDate ?? .distantFuture) }
.map { EventEntity(id: $0.id.uuidString, title: $0.title, dueDate: $0.dueDate,
isRecurring: $0.isRecurring, recurrenceLabel: $0.recurrenceLabel) }
isRecurring: $0.isRecurring, recurrenceLabel: $0.recurrenceLabel,
createdAt: $0.createdAt) }
}
}
@@ -235,7 +237,8 @@ struct EventProvider: AppIntentTimelineProvider {
.filter { !$0.isComplete && ($0.dueDate ?? .distantPast) > now }
.sorted { ($0.dueDate ?? .distantFuture) < ($1.dueDate ?? .distantFuture) }
.map { EventEntity(id: $0.id.uuidString, title: $0.title, dueDate: $0.dueDate,
isRecurring: $0.isRecurring, recurrenceLabel: $0.recurrenceLabel) }
isRecurring: $0.isRecurring, recurrenceLabel: $0.recurrenceLabel,
createdAt: $0.createdAt) }
}
/// The tracked task ("Track Countdown"), but only while it's still live: not
@@ -267,13 +270,16 @@ struct EventProvider: AppIntentTimelineProvider {
/// Resolve the progress window (start target) for any event. Recurring events
/// (Mode B) span the current occurrence period: previous next occurrence
/// so a birthday 5 days away reads ~98% full. One-off events (Mode A) count
/// from an explicit start, the tracking date, or first-seen, up to the due date.
/// from an explicit start, the task's real creation date, the tracking date,
/// or first-seen, up to the due date in that priority order.
private func resolveSpan(due: Date, isRecurring: Bool, label: String?,
anchorKey: String, explicitStart: Date?) -> (start: Date, target: Date) {
anchorKey: String, explicitStart: Date?,
taskCreatedAt: Date? = nil) -> (start: Date, target: Date) {
if isRecurring, let label, let span = Self.recurrenceSpan(label: label, due: due) {
return (span.prev, span.next)
}
if let s = explicitStart, s < due { return (s, due) }
if let created = taskCreatedAt, created < due { return (created, due) }
return (Self.storedAnchor(key: anchorKey, target: due), due)
}
@@ -288,7 +294,8 @@ struct EventProvider: AppIntentTimelineProvider {
let trackedId = task.id.uuidString
let since = UserDefaults.kisani.object(forKey: "kisani.widget.trackedSince") as? Date
let s = resolveSpan(due: task.dueDate ?? now, isRecurring: task.isRecurring,
label: task.recurrenceLabel, anchorKey: trackedId, explicitStart: since)
label: task.recurrenceLabel, anchorKey: trackedId, explicitStart: since,
taskCreatedAt: task.createdAt)
return EventEntry(date: now, eventName: task.title, start: s.start, target: s.target, unitMode: mode)
}
@@ -302,12 +309,13 @@ struct EventProvider: AppIntentTimelineProvider {
}
let ev = upcoming[idx]
let s = resolveSpan(due: due, isRecurring: ev.isRecurring, label: ev.recurrenceLabel,
anchorKey: ev.id, explicitStart: nil)
anchorKey: ev.id, explicitStart: nil, taskCreatedAt: ev.createdAt)
return EventEntry(date: now, eventName: ev.title, start: s.start, target: s.target, unitMode: mode)
}
if let ev = config.event, let due = ev.dueDate {
let s = resolveSpan(due: due, isRecurring: ev.isRecurring, label: ev.recurrenceLabel,
anchorKey: ev.id, explicitStart: config.startDate)
anchorKey: ev.id, explicitStart: config.startDate,
taskCreatedAt: ev.createdAt)
return EventEntry(date: now, eventName: ev.title, start: s.start, target: s.target, unitMode: mode)
}
// Fully custom event (no task behind it).