Give complication timelines minute granularity in the first hour
A compact countdown needs an entry per minute to stay honest. The live Text(style: .timer) it replaces needed none, but renders 1:23:45 — eight characters, which does not fit an accessoryCorner beside its curved label. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d70cdca4e2
commit
9ea1a8705d
@@ -10,14 +10,22 @@ public enum ComplicationTimeline {
|
|||||||
/// Dates at which a complication should re-render.
|
/// Dates at which a complication should re-render.
|
||||||
///
|
///
|
||||||
/// Always begins with `now` so the first entry renders immediately. Event
|
/// Always begins with `now` so the first entry renders immediately. Event
|
||||||
/// starts and ends within `horizon` become entries; hourly marks fill the
|
/// starts and ends within `horizon` become entries.
|
||||||
/// rest so a quiet calendar still advances its date and countdown. The
|
///
|
||||||
/// result is sorted, deduplicated to the second, and capped at
|
/// The first `fineGrainedWindow` then gets a mark every minute, and the rest
|
||||||
|
/// of the horizon one every hour. The fine-grained part is what lets a
|
||||||
|
/// complication show a *compact* countdown — "23m" rather than "1:23:45" —
|
||||||
|
/// which is the only kind that fits an accessoryCorner or a circular gauge.
|
||||||
|
/// A live `Text(style: .timer)` needs no entries at all but is eight
|
||||||
|
/// characters wide, and overlaps the curved label it sits next to.
|
||||||
|
///
|
||||||
|
/// The result is sorted, deduplicated to the second, and capped at
|
||||||
/// `maxEntries`, keeping the earliest.
|
/// `maxEntries`, keeping the earliest.
|
||||||
public static func entryDates(from now: Date,
|
public static func entryDates(from now: Date,
|
||||||
in snapshot: CalendarrSnapshot,
|
in snapshot: CalendarrSnapshot,
|
||||||
horizon: TimeInterval = 24 * 3600,
|
horizon: TimeInterval = 24 * 3600,
|
||||||
maxEntries: Int = 100) -> [Date] {
|
fineGrainedWindow: TimeInterval = 3600,
|
||||||
|
maxEntries: Int = 150) -> [Date] {
|
||||||
let end = now.addingTimeInterval(horizon)
|
let end = now.addingTimeInterval(horizon)
|
||||||
var seen: Set<Int> = []
|
var seen: Set<Int> = []
|
||||||
var dates: [Date] = []
|
var dates: [Date] = []
|
||||||
@@ -36,6 +44,12 @@ public enum ComplicationTimeline {
|
|||||||
add(event.start)
|
add(event.start)
|
||||||
add(event.end)
|
add(event.end)
|
||||||
}
|
}
|
||||||
|
let fineEnd = min(end, now.addingTimeInterval(fineGrainedWindow))
|
||||||
|
var minute = now.addingTimeInterval(60)
|
||||||
|
while minute <= fineEnd {
|
||||||
|
add(minute)
|
||||||
|
minute = minute.addingTimeInterval(60)
|
||||||
|
}
|
||||||
var hourly = now.addingTimeInterval(3600)
|
var hourly = now.addingTimeInterval(3600)
|
||||||
while hourly <= end {
|
while hourly <= end {
|
||||||
add(hourly)
|
add(hourly)
|
||||||
|
|||||||
@@ -73,6 +73,27 @@ struct ComplicationTimelineTests {
|
|||||||
#expect(dates.count >= 24)
|
#expect(dates.count >= 24)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test("The first hour gets minute-granular entries, so a compact countdown stays honest")
|
||||||
|
func minuteGranularFirstHour() {
|
||||||
|
let dates = ComplicationTimeline.entryDates(from: Self.now, in: Self.snapshot([]))
|
||||||
|
let firstHour = dates.filter { $0 <= Self.now.addingTimeInterval(3600) }
|
||||||
|
|
||||||
|
// 60 minute marks plus `now` itself.
|
||||||
|
#expect(firstHour.count >= 60)
|
||||||
|
#expect(firstHour.contains(Self.now.addingTimeInterval(60)))
|
||||||
|
#expect(firstHour.contains(Self.now.addingTimeInterval(1800)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Past the first hour the entries thin out to hourly")
|
||||||
|
func hourlyAfterFirstHour() {
|
||||||
|
let dates = ComplicationTimeline.entryDates(from: Self.now, in: Self.snapshot([]))
|
||||||
|
let later = dates.filter { $0 > Self.now.addingTimeInterval(3600) }
|
||||||
|
|
||||||
|
// 23 further hours, not 23 * 60 minutes.
|
||||||
|
#expect(later.count < 40)
|
||||||
|
#expect(later.contains(Self.now.addingTimeInterval(2 * 3600)))
|
||||||
|
}
|
||||||
|
|
||||||
@Test("The entry count is capped, keeping the earliest entries")
|
@Test("The entry count is capped, keeping the earliest entries")
|
||||||
func capsEntries() {
|
func capsEntries() {
|
||||||
var events: [SnapshotEvent] = []
|
var events: [SnapshotEvent] = []
|
||||||
|
|||||||
Reference in New Issue
Block a user