trends: redraw lines on 7/30/90 selection
Callers previously morphed the existing lines on a reload, so picking a new range only glided in place. Now every range/fuel selection (the only reloads; the sheet has no periodic refetch) replays the staggered per-series draw-in via revealSeries(), so the lines visibly redraw left->right when the user switches 7/30/90. Price<->vs-cheapest still morphs (pure view toggle, no reload). Reduce Motion jump-cuts.
This commit is contained in:
+33
-38
@@ -165,44 +165,13 @@ struct TrendsView: View {
|
|||||||
// pointer probe already done above.
|
// pointer probe already done above.
|
||||||
loadFailed = firstSnapshot == nil
|
loadFailed = firstSnapshot == nil
|
||||||
}
|
}
|
||||||
// Morph vs trace-in: a reload (range/fuel switch) glides the existing
|
// Always redraw the lines on a selection change. `.task(id:)` fires on
|
||||||
// lines to the new data; the first real draw traces each line in
|
// the initial appear and on every fuel/range switch (there's no
|
||||||
// left→right. Reduce Motion jumps straight to the final state.
|
// periodic refetch in this sheet), so a staggered per-series draw-in
|
||||||
let hadData = hasAnyData
|
// replays exactly when the user picks 7/30/90 (or switches fuel) while
|
||||||
if hadData {
|
// a width-stable morph keeps the x-axis/y-range from jumping abruptly.
|
||||||
withAnimation(reduceMotion ? nil : .easeInOut(duration: 0.35)) {
|
series = fetched
|
||||||
series = fetched
|
revealSeries(fetched)
|
||||||
}
|
|
||||||
// Newly-added favourites have no reveal entry yet — surface them
|
|
||||||
// fully so their line isn't left blank by the draw-in waterline.
|
|
||||||
for h in fetched where revealed[h.stationID] == nil {
|
|
||||||
revealed[h.stationID] = h.points.count
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
series = fetched
|
|
||||||
let hasPoints = fetched.contains { !$0.points.isEmpty }
|
|
||||||
if hasPoints {
|
|
||||||
if reduceMotion {
|
|
||||||
var all: [String: Int] = [:]
|
|
||||||
for h in fetched { all[h.stationID] = h.points.count }
|
|
||||||
revealed = all
|
|
||||||
} else {
|
|
||||||
revealed = [:]
|
|
||||||
let cascadeNS = UInt64(0.18 * 1_000_000_000)
|
|
||||||
for (i, h) in fetched.enumerated() {
|
|
||||||
let sid = h.stationID
|
|
||||||
let total = h.points.count
|
|
||||||
Task {
|
|
||||||
try? await Task.sleep(nanoseconds: UInt64(i) * cascadeNS)
|
|
||||||
guard !Task.isCancelled else { return }
|
|
||||||
withAnimation(.easeOut(duration: 0.5)) {
|
|
||||||
revealed[sid] = total
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// A failure with no data IS a connection problem — raise the global
|
// A failure with no data IS a connection problem — raise the global
|
||||||
// banner so the user isn't stuck with a silent retry state. Success
|
// banner so the user isn't stuck with a silent retry state. Success
|
||||||
// clears it (only if the banner is the connection banner).
|
// clears it (only if the banner is the connection banner).
|
||||||
@@ -213,6 +182,32 @@ struct TrendsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Replays the staggered per-series draw-in for the given histories:
|
||||||
|
/// resets the waterline, then cascades each station's line left→right,
|
||||||
|
/// 0.18s apart, on every range/fuel selection. Reduce Motion jump-cuts
|
||||||
|
/// straight to the full state.
|
||||||
|
private func revealSeries(_ histories: [StationHistory]) {
|
||||||
|
let hasPoints = histories.contains { !$0.points.isEmpty }
|
||||||
|
guard hasPoints else { return }
|
||||||
|
if reduceMotion {
|
||||||
|
revealed = histories.reduce(into: [:]) { $0[$1.stationID] = $1.points.count }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
revealed = [:]
|
||||||
|
let cascadeNS = UInt64(0.18 * 1_000_000_000)
|
||||||
|
for (i, h) in histories.enumerated() {
|
||||||
|
let sid = h.stationID
|
||||||
|
let total = h.points.count
|
||||||
|
Task {
|
||||||
|
try? await Task.sleep(nanoseconds: UInt64(i) * cascadeNS)
|
||||||
|
guard !Task.isCancelled else { return }
|
||||||
|
withAnimation(.easeOut(duration: 0.5)) {
|
||||||
|
revealed[sid] = total
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func yLabel(_ pence: Double) -> String {
|
private func yLabel(_ pence: Double) -> String {
|
||||||
switch mode {
|
switch mode {
|
||||||
case .price:
|
case .price:
|
||||||
|
|||||||
Reference in New Issue
Block a user