From 76033311d2353bd4b97aecf2f22ef20f7a15f929 Mon Sep 17 00:00:00 2001 From: Gordon Beeming Date: Wed, 28 Oct 2026 10:41:16 +1000 Subject: [PATCH 1/2] Show every reset and Perk on the Resets & Perks page The sidebar page was a placeholder, and the Overview only lists its range. The page now opens on All time with its own range control, the session switch, the chart and the full list. --- Sources/BarUI/ContentView.swift | 4 +- Sources/BarUI/ResetsAndPerksPageView.swift | 49 ++++++++++++++++++++++ Sources/BarUI/ResetsAndPerksSection.swift | 13 ++++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 Sources/BarUI/ResetsAndPerksPageView.swift diff --git a/Sources/BarUI/ContentView.swift b/Sources/BarUI/ContentView.swift index 0637650..db5c276 100644 --- a/Sources/BarUI/ContentView.swift +++ b/Sources/BarUI/ContentView.swift @@ -126,7 +126,9 @@ struct ContentView: View { OverviewView(overview: archive.overview, archiveFailure: archive.failure) case .barApp(let app): BarAppPageView(app: app, status: monitor.status(of: app), home: Self.home, remote: remote, skipped: archive.overview?.skippedRecords[app], history: archive.history, overview: archive.overview) - case .archive: + case .archive(.events): + ResetsAndPerksPageView(overview: archive.overview, archiveFailure: archive.failure) + case .archive(.imports): PlaceholderPageView(item: item) } } diff --git a/Sources/BarUI/ResetsAndPerksPageView.swift b/Sources/BarUI/ResetsAndPerksPageView.swift new file mode 100644 index 0000000..4e9a887 --- /dev/null +++ b/Sources/BarUI/ResetsAndPerksPageView.swift @@ -0,0 +1,49 @@ +import BarUICore +import SwiftUI + +/// Every reset, Perk change and Tracking change in the Archive. The Overview +/// shows the same section for its range; this page opens on All time so +/// nothing older is out of reach. +struct ResetsAndPerksPageView: View { + /// Nil when the Archive couldn't be opened; `archiveFailure` says why. + let overview: LimitsOverview? + let archiveFailure: String? + + @State private var range: TimeRange = .all + /// Who this view is to the overview, which reads as far back as the + /// longest range any view shows. + @State private var viewer = UUID() + + var body: some View { + Group { + if let overview { + TimelineView(.periodic(from: .now, by: 30)) { context in + ScrollView { + ResetsAndPerksSection( + events: overview.events(range: range, now: context.date), + range: range, + now: context.date, + tokensDomain: nil, + rangeSelection: $range + ) + .padding(.horizontal, 32) + .padding(.vertical, 16) + } + } + .task(id: range) { + await overview.show(range, by: viewer) + } + .onDisappear { + overview.stopShowing(by: viewer) + } + } else { + ContentUnavailableView( + "The Archive couldn't be opened", + systemImage: "externaldrive.badge.exclamationmark", + description: Text(archiveFailure ?? "") + ) + } + } + .navigationTitle(ArchivePage.events.title) + } +} diff --git a/Sources/BarUI/ResetsAndPerksSection.swift b/Sources/BarUI/ResetsAndPerksSection.swift index 44242b0..1527873 100644 --- a/Sources/BarUI/ResetsAndPerksSection.swift +++ b/Sources/BarUI/ResetsAndPerksSection.swift @@ -10,6 +10,9 @@ struct ResetsAndPerksSection: View { let now: Date /// The Tokens chart's span, so this chart's days sit under the same days. let tokensDomain: ClosedRange? + /// The page's own range control; nil on the Overview, where the Tokens + /// chart's control drives both sections. + var rangeSelection: Binding? /// Off by default: a 5-hour session resets several times a day, and those /// rows would bury the weekly resets and everything else. @@ -27,6 +30,16 @@ struct ResetsAndPerksSection: View { Text("Resets & Perks") .font(.headline) Spacer() + if let rangeSelection { + Picker("Range", selection: rangeSelection) { + ForEach(TimeRange.allCases) { range in + Text(range.label).tag(range) + } + } + .pickerStyle(.segmented) + .labelsHidden() + .fixedSize() + } Toggle("Show session resets", isOn: $showSessionResets) .toggleStyle(.switch) .controlSize(.small) From 4bd2f9046f2410fd3d2996d8f78592f53c6e843b Mon Sep 17 00:00:00 2001 From: Gordon Beeming Date: Wed, 28 Oct 2026 10:44:37 +1000 Subject: [PATCH 2/2] Say when the Archive read failed on the Resets & Perks page A failed read keeps the last events, so the page shows the problem the way the Overview does instead of passing a short history off as the whole Archive. --- Sources/BarUI/OverviewView.swift | 2 +- Sources/BarUI/ResetsAndPerksPageView.swift | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Sources/BarUI/OverviewView.swift b/Sources/BarUI/OverviewView.swift index e8c25bc..6f91eb5 100644 --- a/Sources/BarUI/OverviewView.swift +++ b/Sources/BarUI/OverviewView.swift @@ -71,7 +71,7 @@ struct OverviewView: View { } } -private struct ProblemLabel: View { +struct ProblemLabel: View { let problem: String var body: some View { diff --git a/Sources/BarUI/ResetsAndPerksPageView.swift b/Sources/BarUI/ResetsAndPerksPageView.swift index 4e9a887..25d8aac 100644 --- a/Sources/BarUI/ResetsAndPerksPageView.swift +++ b/Sources/BarUI/ResetsAndPerksPageView.swift @@ -19,13 +19,20 @@ struct ResetsAndPerksPageView: View { if let overview { TimelineView(.periodic(from: .now, by: 30)) { context in ScrollView { - ResetsAndPerksSection( - events: overview.events(range: range, now: context.date), - range: range, - now: context.date, - tokensDomain: nil, - rangeSelection: $range - ) + VStack(alignment: .leading, spacing: 20) { + // A failed read keeps the last events, so without + // this a short history would pass for the whole Archive. + if let problem = overview.problem { + ProblemLabel(problem: problem) + } + ResetsAndPerksSection( + events: overview.events(range: range, now: context.date), + range: range, + now: context.date, + tokensDomain: nil, + rangeSelection: $range + ) + } .padding(.horizontal, 32) .padding(.vertical, 16) }