Live tab: restore type filter via bundled aircraft DB + sheet pickers

Two issues:

1) Menu list stutters. SwiftUI's Menu renders all rows in a
   non-virtualized popover. With 30+ airlines and Buttons containing
   Labels, scrolling jank starts immediately. Switched the airline +
   type filters to a sheet-based picker (LiveFilterPicker) backed by
   a real List — virtualized scrolling, plus a search bar that
   filters as you type.

2) Type filter was non-functional because OpenSky's anonymous tier
   returns ADS-B emitter category as null for most aircraft.
   Replaced with a real type-code lookup: bundled aircraftDB.json
   (1.5MB slimmed copy of OpenSky's aircraft metadata, 100k
   commercial-class airframes, filtered to skip GA / gliders /
   ultralights). AircraftDatabase.shared.typeCode(forICAO24:)
   returns the ICAO type designator (B738, A21N, etc.).
   AircraftDatabase.displayName(forTypeCode:) maps the top ~130
   common codes to friendly names ("B738" → "Boeing 737-800").

LiveAircraft now exposes a `typeCode` computed property that
indexes into the DB. The type filter chip → sheet flow uses the
same LiveFilterPicker as airlines, with multi-select + counts +
search.

Both pickers keep the "Selected (N)" group pinned at the top so
the user always sees what they have active without scrolling.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey T
2026-05-27 07:21:49 -05:00
parent d6fb73db2c
commit 390a158487
6 changed files with 406 additions and 35 deletions
+12
View File
@@ -55,6 +55,9 @@
LV7700007777000077770001 /* OpenSkyCredentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = LV7700007777000077770002 /* OpenSkyCredentials.swift */; };
LV8800008888000088880001 /* OpenSkySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = LV8800008888000088880002 /* OpenSkySettingsView.swift */; };
LV9900009999000099990001 /* airlines.json in Resources */ = {isa = PBXBuildFile; fileRef = LV9900009999000099990002 /* airlines.json */; };
LVAA000AAAA000AAAA000001 /* AircraftDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = LVAA000AAAA000AAAA000002 /* AircraftDatabase.swift */; };
LVBB000BBBB000BBBB000001 /* LiveFilterPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = LVBB000BBBB000BBBB000002 /* LiveFilterPicker.swift */; };
LVCC000CCCC000CCCC000001 /* aircraftDB.json in Resources */ = {isa = PBXBuildFile; fileRef = LVCC000CCCC000CCCC000002 /* aircraftDB.json */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -118,6 +121,9 @@
LV7700007777000077770002 /* OpenSkyCredentials.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSkyCredentials.swift; sourceTree = "<group>"; };
LV8800008888000088880002 /* OpenSkySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSkySettingsView.swift; sourceTree = "<group>"; };
LV9900009999000099990002 /* airlines.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = airlines.json; sourceTree = "<group>"; };
LVAA000AAAA000AAAA000002 /* AircraftDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AircraftDatabase.swift; sourceTree = "<group>"; };
LVBB000BBBB000BBBB000002 /* LiveFilterPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveFilterPicker.swift; sourceTree = "<group>"; };
LVCC000CCCC000CCCC000002 /* aircraftDB.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = aircraftDB.json; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -155,6 +161,7 @@
LV5500005555000055550002 /* LiveFlightDetailSheet.swift */,
LV6600006666000066660002 /* RootView.swift */,
LV8800008888000088880002 /* OpenSkySettingsView.swift */,
LVBB000BBBB000BBBB000002 /* LiveFilterPicker.swift */,
AA5555555555555555555555 /* Styles */,
AA6666666666666666666666 /* Components */,
);
@@ -190,6 +197,7 @@
D9E26DCDE2904210ABCA7855 /* Assets.xcassets */,
53F457716F0642BDBCBA93EA /* airports.json */,
LV9900009999000099990002 /* airlines.json */,
LVCC000CCCC000CCCC000002 /* aircraftDB.json */,
);
path = Flights;
sourceTree = "<group>";
@@ -234,6 +242,7 @@
LV2200002222000022220002 /* OpenSkyClient.swift */,
LV3300003333000033330002 /* AircraftRegistry.swift */,
LV7700007777000077770002 /* OpenSkyCredentials.swift */,
LVAA000AAAA000AAAA000002 /* AircraftDatabase.swift */,
);
path = Services;
sourceTree = "<group>";
@@ -348,6 +357,7 @@
F79789179F4443FD859BDEF0 /* Assets.xcassets in Resources */,
80D2BC95002A4931B3C10B4C /* airports.json in Resources */,
LV9900009999000099990001 /* airlines.json in Resources */,
LVCC000CCCC000CCCC000001 /* aircraftDB.json in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -402,6 +412,8 @@
LV6600006666000066660001 /* RootView.swift in Sources */,
LV7700007777000077770001 /* OpenSkyCredentials.swift in Sources */,
LV8800008888000088880001 /* OpenSkySettingsView.swift in Sources */,
LVAA000AAAA000AAAA000001 /* AircraftDatabase.swift in Sources */,
LVBB000BBBB000BBBB000001 /* LiveFilterPicker.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};