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:
@@ -20,13 +20,24 @@ import CoreLocation
|
|||||||
/// 5. Bottom drawer is a real swipe-up sheet with detents — peek
|
/// 5. Bottom drawer is a real swipe-up sheet with detents — peek
|
||||||
/// shows the count + replay; expanded shows filter chips.
|
/// shows the count + replay; expanded shows filter chips.
|
||||||
struct HistoryRouteMapView: View {
|
struct HistoryRouteMapView: View {
|
||||||
let flights: [LoggedFlight] // filtered
|
/// The user's full unfiltered log. The map filters this set
|
||||||
let allFlights: [LoggedFlight] // unfiltered (kept for parent-API compat)
|
/// 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 database: AirportDatabase
|
||||||
let openSky: OpenSkyClient
|
let openSky: OpenSkyClient
|
||||||
let store: FlightHistoryStore
|
let store: FlightHistoryStore
|
||||||
@Binding var filters: HistoryFilters
|
@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 position: MapCameraPosition = .automatic
|
||||||
@State private var progress: Double = 0
|
@State private var progress: Double = 0
|
||||||
@State private var animationKey: Int = 0
|
@State private var animationKey: Int = 0
|
||||||
|
|||||||
@@ -102,7 +102,13 @@ struct HistoryView: View {
|
|||||||
}
|
}
|
||||||
Section("Explore") {
|
Section("Explore") {
|
||||||
Button { showingPassport = true } label: { Label("Passport", systemImage: "book.closed") }
|
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 { showingAircraftStats = true } label: { Label("Aircraft stats", systemImage: "airplane.circle") }
|
||||||
Button { showingYearInReview = true } label: { Label("Year in Review", systemImage: "sparkles") }
|
Button { showingYearInReview = true } label: { Label("Year in Review", systemImage: "sparkles") }
|
||||||
}
|
}
|
||||||
@@ -123,7 +129,6 @@ struct HistoryView: View {
|
|||||||
.sheet(isPresented: $showingMap) {
|
.sheet(isPresented: $showingMap) {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
HistoryRouteMapView(
|
HistoryRouteMapView(
|
||||||
flights: scoped,
|
|
||||||
allFlights: flights,
|
allFlights: flights,
|
||||||
database: database,
|
database: database,
|
||||||
openSky: openSky,
|
openSky: openSky,
|
||||||
@@ -285,7 +290,10 @@ struct HistoryView: View {
|
|||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var quickLinks: some View {
|
private var quickLinks: some View {
|
||||||
HStack(spacing: 10) {
|
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: "Aircraft", icon: "airplane.circle.fill") { showingAircraftStats = true }
|
||||||
quickLink(title: "Year", icon: "sparkles") { showingYearInReview = true }
|
quickLink(title: "Year", icon: "sparkles") { showingYearInReview = true }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user