Route map: filter from allFlights + filters internally

When the user opened the map and picked a year (e.g. 2026) in the
in-map filter sheet, the map kept drawing every flight instead of
just 2026. Root cause: the map was taking a pre-filtered `flights`
prop from HistoryView via the .sheet content closure. SwiftUI does
propagate the `filters` Binding into the sheet correctly, but it
doesn't reliably re-render the .sheet content closure with the new
pre-filtered prop value when the parent rerenders mid-presentation —
so .onChange(of: filters) fires, reset() runs, but `self.flights`
is the old (unfiltered) prop value, and the rebuilt schedule still
includes every flight.

Fix: stop relying on the parent doing the filtering. The map now
takes `allFlights` (unfiltered) and computes the displayed set
itself via `allFlights.filter { filters.matches($0) }`. Since
`filters` is a Binding that propagates cleanly, the computed set
is always in sync with whatever the user just selected in the
filter sheet — no parent-render-order race.

Also: when opening the map from HistoryView's toolbar or quick-link
card, fold the year-strip `selectedYear` into `filters.years` so
the map opens scoped to whatever the user was browsing on the home
list.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Trey T
2026-05-31 13:05:21 -05:00
parent 0c9d02f7d4
commit cb981c5380
2 changed files with 24 additions and 5 deletions
+13 -2
View File
@@ -20,13 +20,24 @@ import CoreLocation
/// 5. Bottom drawer is a real swipe-up sheet with detents peek
/// shows the count + replay; expanded shows filter chips.
struct HistoryRouteMapView: View {
let flights: [LoggedFlight] // filtered
let allFlights: [LoggedFlight] // unfiltered (kept for parent-API compat)
/// The user's full unfiltered log. The map filters this set
/// internally by `filters` so changes from the in-map filter sheet
/// reliably re-scope the animation, even if the parent's
/// .sheet content closure doesn't propagate a new pre-filtered
/// array to us in time.
let allFlights: [LoggedFlight]
let database: AirportDatabase
let openSky: OpenSkyClient
let store: FlightHistoryStore
@Binding var filters: HistoryFilters
/// Locally-derived "what to draw" set. Recomputed on every body
/// pass; cheap because it's a single array filter on ~hundreds of
/// items at most.
private var flights: [LoggedFlight] {
allFlights.filter { filters.matches($0) }
}
@State private var position: MapCameraPosition = .automatic
@State private var progress: Double = 0
@State private var animationKey: Int = 0
+11 -3
View File
@@ -102,7 +102,13 @@ struct HistoryView: View {
}
Section("Explore") {
Button { showingPassport = true } label: { Label("Passport", systemImage: "book.closed") }
Button { showingMap = true } label: { Label("Route map", systemImage: "map.fill") }
Button {
// Carry the year-strip scope into the map's
// filter binding so the map opens showing
// the same year the user was browsing.
if let y = selectedYear { filters.years = [y] }
showingMap = true
} label: { Label("Route map", systemImage: "map.fill") }
Button { showingAircraftStats = true } label: { Label("Aircraft stats", systemImage: "airplane.circle") }
Button { showingYearInReview = true } label: { Label("Year in Review", systemImage: "sparkles") }
}
@@ -123,7 +129,6 @@ struct HistoryView: View {
.sheet(isPresented: $showingMap) {
NavigationStack {
HistoryRouteMapView(
flights: scoped,
allFlights: flights,
database: database,
openSky: openSky,
@@ -285,7 +290,10 @@ struct HistoryView: View {
@ViewBuilder
private var quickLinks: some View {
HStack(spacing: 10) {
quickLink(title: "Map", icon: "map.fill") { showingMap = true }
quickLink(title: "Map", icon: "map.fill") {
if let y = selectedYear { filters.years = [y] }
showingMap = true
}
quickLink(title: "Aircraft", icon: "airplane.circle.fill") { showingAircraftStats = true }
quickLink(title: "Year", icon: "sparkles") { showingYearInReview = true }
}