Skip to content

Commit

Permalink
feat: show filtered departures in stop details (#124)
Browse files Browse the repository at this point in the history
* implement filter

* rethink binding one more time

* add tests

* rename NearbyStopRoutePatternView to HeadsignRowView

* also move UpcomingTripView into ComponentViews

* rearrange splitPerTrip

* filter to route in FilteredRouteView constructor

* make stack deeper for lastStopDetailsFilter test
  • Loading branch information
boringcactus authored Apr 10, 2024
1 parent 65c27e1 commit f7ab56e
Show file tree
Hide file tree
Showing 18 changed files with 477 additions and 32 deletions.
38 changes: 33 additions & 5 deletions iosApp/iosApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@
6EF50F512B988BF600833070 /* PredictionsFetcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EF50F502B988BF600833070 /* PredictionsFetcherTests.swift */; };
7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; };
8C5054582BB5EB6C00C6A51C /* StopDetailsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C5054572BB5EB6C00C6A51C /* StopDetailsPage.swift */; };
8C6A48402BC09A2E0032A554 /* StopDetailsFilteredRouteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C6A483F2BC09A2E0032A554 /* StopDetailsFilteredRouteView.swift */; };
8C7FA86F2B5EEA34009B699D /* LocationDataManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7FA86E2B5EEA34009B699D /* LocationDataManagerTests.swift */; };
8C7FA8712B5F2EF2009B699D /* NearbyTransitViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7FA8702B5F2EF2009B699D /* NearbyTransitViewTests.swift */; };
8C7FA8732B5F36D6009B699D /* Backend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7FA8722B5F36D6009B699D /* Backend.swift */; };
8C84D33E2B5AEE0200192C0A /* NearbyTransitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C84D33D2B5AEE0200192C0A /* NearbyTransitView.swift */; };
8CB823D62BC5E85C002C87E0 /* SheetNavigationStackEntryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CB823D52BC5E85C002C87E0 /* SheetNavigationStackEntryTests.swift */; };
8CB823D92BC5EDD2002C87E0 /* StopDetailsRouteViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CB823D82BC5EDD2002C87E0 /* StopDetailsRouteViewTests.swift */; };
8CB823DB2BC5F053002C87E0 /* StopDetailsRoutesViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CB823DA2BC5F053002C87E0 /* StopDetailsRoutesViewTests.swift */; };
8CB823DD2BC5F432002C87E0 /* StopDetailsFilteredRouteViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CB823DC2BC5F432002C87E0 /* StopDetailsFilteredRouteViewTests.swift */; };
8CC1BB402B59D1F6005386FE /* LocationDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC1BB3F2B59D1F6005386FE /* LocationDataManager.swift */; };
8CD1F8CD2B7164C100F419D4 /* PredictionsFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1F8CC2B7164C100F419D4 /* PredictionsFetcher.swift */; };
8CE0140E2BBDB7C300918FAE /* RoutePillSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE0140D2BBDB7C300918FAE /* RoutePillSection.swift */; };
Expand All @@ -47,7 +52,7 @@
9A1631E62B76CAB400F667F4 /* GlobalFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1631E52B76CAB400F667F4 /* GlobalFetcher.swift */; };
9A2005C52B97B5EA00F562E1 /* NearbyRouteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2005C42B97B5EA00F562E1 /* NearbyRouteView.swift */; };
9A2005C72B97B63300F562E1 /* NearbyStopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2005C62B97B63300F562E1 /* NearbyStopView.swift */; };
9A2005C92B97B65900F562E1 /* NearbyStopRoutePatternView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2005C82B97B65900F562E1 /* NearbyStopRoutePatternView.swift */; };
9A2005C92B97B65900F562E1 /* HeadsignRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2005C82B97B65900F562E1 /* HeadsignRowView.swift */; };
9A2005CB2B97B68700F562E1 /* UpcomingTripView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2005CA2B97B68700F562E1 /* UpcomingTripView.swift */; };
9A37F3052BACCC40001714FE /* DoubleRoundedExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A37F3042BACCC40001714FE /* DoubleRoundedExtension.swift */; };
9A37F3072BACCCA5001714FE /* CoordinateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A37F3062BACCCA5001714FE /* CoordinateExtension.swift */; };
Expand Down Expand Up @@ -157,10 +162,15 @@
8C349BB72B754F2600AC7FFB /* 10 Park Plaza.gpx */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "10 Park Plaza.gpx"; sourceTree = "<group>"; };
8C42F06F2B890BC800F9A77B /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = "<group>"; };
8C5054572BB5EB6C00C6A51C /* StopDetailsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StopDetailsPage.swift; sourceTree = "<group>"; };
8C6A483F2BC09A2E0032A554 /* StopDetailsFilteredRouteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StopDetailsFilteredRouteView.swift; sourceTree = "<group>"; };
8C7FA86E2B5EEA34009B699D /* LocationDataManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationDataManagerTests.swift; sourceTree = "<group>"; };
8C7FA8702B5F2EF2009B699D /* NearbyTransitViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbyTransitViewTests.swift; sourceTree = "<group>"; };
8C7FA8722B5F36D6009B699D /* Backend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Backend.swift; sourceTree = "<group>"; };
8C84D33D2B5AEE0200192C0A /* NearbyTransitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbyTransitView.swift; sourceTree = "<group>"; };
8CB823D52BC5E85C002C87E0 /* SheetNavigationStackEntryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetNavigationStackEntryTests.swift; sourceTree = "<group>"; };
8CB823D82BC5EDD2002C87E0 /* StopDetailsRouteViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StopDetailsRouteViewTests.swift; sourceTree = "<group>"; };
8CB823DA2BC5F053002C87E0 /* StopDetailsRoutesViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StopDetailsRoutesViewTests.swift; sourceTree = "<group>"; };
8CB823DC2BC5F432002C87E0 /* StopDetailsFilteredRouteViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StopDetailsFilteredRouteViewTests.swift; sourceTree = "<group>"; };
8CC1BB3F2B59D1F6005386FE /* LocationDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationDataManager.swift; sourceTree = "<group>"; };
8CD1F8CC2B7164C100F419D4 /* PredictionsFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PredictionsFetcher.swift; sourceTree = "<group>"; };
8CE0140D2BBDB7C300918FAE /* RoutePillSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoutePillSection.swift; sourceTree = "<group>"; };
Expand All @@ -175,7 +185,7 @@
9A1631E52B76CAB400F667F4 /* GlobalFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalFetcher.swift; sourceTree = "<group>"; };
9A2005C42B97B5EA00F562E1 /* NearbyRouteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbyRouteView.swift; sourceTree = "<group>"; };
9A2005C62B97B63300F562E1 /* NearbyStopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbyStopView.swift; sourceTree = "<group>"; };
9A2005C82B97B65900F562E1 /* NearbyStopRoutePatternView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbyStopRoutePatternView.swift; sourceTree = "<group>"; };
9A2005C82B97B65900F562E1 /* HeadsignRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadsignRowView.swift; sourceTree = "<group>"; };
9A2005CA2B97B68700F562E1 /* UpcomingTripView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpcomingTripView.swift; sourceTree = "<group>"; };
9A37F3042BACCC40001714FE /* DoubleRoundedExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoubleRoundedExtension.swift; sourceTree = "<group>"; };
9A37F3062BACCCA5001714FE /* CoordinateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoordinateExtension.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -409,6 +419,17 @@
8C5054572BB5EB6C00C6A51C /* StopDetailsPage.swift */,
8CE0140F2BBDB8DC00918FAE /* StopDetailsRoutesView.swift */,
8CE014112BBDB96900918FAE /* StopDetailsRouteView.swift */,
8C6A483F2BC09A2E0032A554 /* StopDetailsFilteredRouteView.swift */,
);
path = StopDetails;
sourceTree = "<group>";
};
8CB823D72BC5EDBF002C87E0 /* StopDetails */ = {
isa = PBXGroup;
children = (
8CB823D82BC5EDD2002C87E0 /* StopDetailsRouteViewTests.swift */,
8CB823DA2BC5F053002C87E0 /* StopDetailsRoutesViewTests.swift */,
8CB823DC2BC5F432002C87E0 /* StopDetailsFilteredRouteViewTests.swift */,
);
path = StopDetails;
sourceTree = "<group>";
Expand All @@ -430,8 +451,6 @@
8C84D33D2B5AEE0200192C0A /* NearbyTransitView.swift */,
9A2005C42B97B5EA00F562E1 /* NearbyRouteView.swift */,
9A2005C62B97B63300F562E1 /* NearbyStopView.swift */,
9A2005C82B97B65900F562E1 /* NearbyStopRoutePatternView.swift */,
9A2005CA2B97B68700F562E1 /* UpcomingTripView.swift */,
9AC4FDEE2BACE1EC004479BF /* NearbyTransitLocationProvider.swift */,
9AC4FDF02BACE216004479BF /* NearbyTransitPageView.swift */,
);
Expand Down Expand Up @@ -467,13 +486,16 @@
9A4E8E582B7EC4B90066B936 /* RoutePill.swift */,
9AB44A102B8FC43E00E8FFB3 /* IconCard.swift */,
8CE0140D2BBDB7C300918FAE /* RoutePillSection.swift */,
9A2005C82B97B65900F562E1 /* HeadsignRowView.swift */,
9A2005CA2B97B68700F562E1 /* UpcomingTripView.swift */,
);
path = ComponentViews;
sourceTree = "<group>";
};
9A5B275D2BB242EF009A6FC6 /* Pages */ = {
isa = PBXGroup;
children = (
8CB823D72BC5EDBF002C87E0 /* StopDetails */,
9A5B275E2BB24326009A6FC6 /* Map */,
);
path = Pages;
Expand Down Expand Up @@ -532,6 +554,7 @@
isa = PBXGroup;
children = (
9ADB849F2BAD1B84006581CE /* DebouncerTests.swift */,
8CB823D52BC5E85C002C87E0 /* SheetNavigationStackEntryTests.swift */,
);
path = Utils;
sourceTree = "<group>";
Expand Down Expand Up @@ -820,7 +843,9 @@
9AD1D1FE2BA4D5C600182060 /* ViewportProviderTest.swift in Sources */,
9A887D592B698EF1006F5B80 /* SearchResultViewTests.swift in Sources */,
9A5B27602BB31178009A6FC6 /* StopSourceGeneratorTests.swift in Sources */,
8CB823D62BC5E85C002C87E0 /* SheetNavigationStackEntryTests.swift in Sources */,
6EE745842B965B9C0052227E /* SocketTests.swift in Sources */,
8CB823DD2BC5F432002C87E0 /* StopDetailsFilteredRouteViewTests.swift in Sources */,
9A5B27682BB36A23009A6FC6 /* StopLayerGeneratorTests.swift in Sources */,
8CEA10272BA4C83D001C6EB9 /* AlertsFetcherTests.swift in Sources */,
6EF50F482B9889D600833070 /* MockSocket.swift in Sources */,
Expand All @@ -838,7 +863,9 @@
8C7FA86F2B5EEA34009B699D /* LocationDataManagerTests.swift in Sources */,
6EED5E8F2B3DC6A00052A1B8 /* IosAppTests.swift in Sources */,
8C7FA8712B5F2EF2009B699D /* NearbyTransitViewTests.swift in Sources */,
8CB823DB2BC5F053002C87E0 /* StopDetailsRoutesViewTests.swift in Sources */,
6E4EACFC2B7A82AC0011AB8B /* MockLocationFetcher.swift in Sources */,
8CB823D92BC5EDD2002C87E0 /* StopDetailsRouteViewTests.swift in Sources */,
6EE745882B965C2B0052227E /* MessageTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -873,7 +900,7 @@
9AB44A132B911E6400E8FFB3 /* DateExtension.swift in Sources */,
6EE745862B965C130052227E /* Message.swift in Sources */,
9A9E05F62B6D6EF70086B437 /* NearbyFetcher.swift in Sources */,
9A2005C92B97B65900F562E1 /* NearbyStopRoutePatternView.swift in Sources */,
9A2005C92B97B65900F562E1 /* HeadsignRowView.swift in Sources */,
9A4E8E592B7EC4B90066B936 /* RoutePill.swift in Sources */,
8CE014142BBDBE5200918FAE /* BackendProvider.swift in Sources */,
9A5B27562BB221C1009A6FC6 /* RouteLayerGenerator.swift in Sources */,
Expand Down Expand Up @@ -907,6 +934,7 @@
9A5830562BA3A2CE0039876E /* ViewportExtension.swift in Sources */,
9A5830582BA4A1A30039876E /* ViewportProvider.swift in Sources */,
9A5B27522BB1EF45009A6FC6 /* StopSourceGenerator.swift in Sources */,
8C6A48402BC09A2E0032A554 /* StopDetailsFilteredRouteView.swift in Sources */,
9AC4FDEF2BACE1EC004479BF /* NearbyTransitLocationProvider.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// NearbyStopRoutePatternView.swift
// HeadsignRowView.swift
// iosApp
//
// Created by Simon, Emma on 3/5/24.
Expand All @@ -9,7 +9,7 @@
import shared
import SwiftUI

struct NearbyStopRoutePatternView: View {
struct HeadsignRowView: View {
let headsign: String
let predictions: PatternsByHeadsign.Format

Expand Down Expand Up @@ -43,16 +43,18 @@ struct NearbyStopRoutePatternView_Previews: PreviewProvider {
prediction.trip = trip
prediction.departureTime = now.addingTimeInterval(5 * 60).toKotlinInstant()
}
NearbyStopRoutePatternView(headsign: "Some", predictions: PatternsByHeadsign.FormatSome(trips: [
.init(trip: .init(trip: trip, prediction: prediction), now: now.toKotlinInstant()),
]))
NearbyStopRoutePatternView(headsign: "None", predictions: PatternsByHeadsign.FormatNone.shared)
NearbyStopRoutePatternView(headsign: "Loading", predictions: PatternsByHeadsign.FormatLoading.shared)
NearbyStopRoutePatternView(headsign: "No Service", predictions: PatternsByHeadsign.FormatNoService(alert:
ObjectCollectionBuilder.Single.shared.alert { alert in
alert.effect = .suspension
}
))
List {
HeadsignRowView(headsign: "Some", predictions: PatternsByHeadsign.FormatSome(trips: [
.init(trip: .init(trip: trip, prediction: prediction), now: now.toKotlinInstant()),
]))
HeadsignRowView(headsign: "None", predictions: PatternsByHeadsign.FormatNone.shared)
HeadsignRowView(headsign: "Loading", predictions: PatternsByHeadsign.FormatLoading.shared)
HeadsignRowView(headsign: "No Service", predictions: PatternsByHeadsign.FormatNoService(
alert: ObjectCollectionBuilder.Single.shared.alert { alert in
alert.effect = .suspension
}
))
}
}
}
}
4 changes: 2 additions & 2 deletions iosApp/iosApp/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ struct ContentView: View {
.navigationBarHidden(true)
.navigationDestination(for: SheetNavigationStackEntry.self) { entry in
switch entry {
case let .stopDetails(stop, filter):
case let .stopDetails(stop, _):
StopDetailsPage(
backend: backendProvider.backend,
socket: socketProvider.socket,
globalFetcher: globalFetcher,
stop: stop, filter: filter
stop: stop, filter: $navigationStack.lastStopDetailsFilter
)
}
}
Expand Down
3 changes: 3 additions & 0 deletions iosApp/iosApp/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
},
"BRD" : {

},
"Clear Filter" : {

},
"Couldn't load nearby transit, connection was interrupted" : {

Expand Down
2 changes: 1 addition & 1 deletion iosApp/iosApp/Pages/NearbyTransit/NearbyStopView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct NearbyStopView: View {
patternsAtStop.stop,
.init(routeId: patternsAtStop.route.id, directionId: patternsByHeadsign.directionId())
)) {
NearbyStopRoutePatternView(
HeadsignRowView(
headsign: patternsByHeadsign.headsign,
predictions: patternsByHeadsign.format(now: now)
)
Expand Down
65 changes: 65 additions & 0 deletions iosApp/iosApp/Pages/StopDetails/StopDetailsFilteredRouteView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//
// StopDetailsFilteredRouteView.swift
// iosApp
//
// Created by Horn, Melody on 2024-04-05.
// Copyright © 2024 MBTA. All rights reserved.
//

import Foundation
import shared
import SwiftUI

struct StopDetailsFilteredRouteView: View {
let patternsByStop: PatternsByStop
let now: Instant
@Binding var filter: StopDetailsFilter?

struct RowData {
let tripId: String
let headsign: String
let formatted: PatternsByHeadsign.Format

init?(trip: UpcomingTrip, route: Route, expectedDirection: Int32?, now: Instant) {
if trip.trip.directionId != expectedDirection {
return nil
}

tripId = trip.trip.id
headsign = trip.trip.headsign
formatted = PatternsByHeadsign(
route: route, headsign: headsign, patterns: [], upcomingTrips: [trip], alertsHere: nil
).format(now: now)

if !(formatted is PatternsByHeadsign.FormatSome) {
return nil
}
}
}

let rows: [RowData]

init(departures: StopDetailsDepartures, now: Instant, filter filterBinding: Binding<StopDetailsFilter?>) {
_filter = filterBinding
let filter = filterBinding.wrappedValue
let patternsByStop = departures.routes.first(where: { $0.route.id == filter?.routeId })!
self.patternsByStop = patternsByStop
self.now = now

let expectedDirection: Int32? = filter?.directionId
rows = patternsByStop.allUpcomingTrips().compactMap {
RowData(trip: $0, route: patternsByStop.route, expectedDirection: expectedDirection, now: now)
}
}

var body: some View {
Button(action: { filter = nil }, label: { Text("Clear Filter") })
List {
RoutePillSection(route: patternsByStop.route) {
ForEach(rows, id: \.tripId) { row in
HeadsignRowView(headsign: row.headsign, predictions: row.formatted)
}
}
}
}
}
8 changes: 4 additions & 4 deletions iosApp/iosApp/Pages/StopDetails/StopDetailsPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@ struct StopDetailsPage: View {
@StateObject var scheduleFetcher: ScheduleFetcher
@StateObject var predictionsFetcher: PredictionsFetcher
var stop: Stop
var filter: StopDetailsFilter?
@Binding var filter: StopDetailsFilter?
@State var now = Date.now

let timer = Timer.publish(every: 5, on: .main, in: .common).autoconnect()

init(backend: any BackendProtocol, socket: any PhoenixSocket, globalFetcher: GlobalFetcher,
stop: Stop, filter: StopDetailsFilter?) {
stop: Stop, filter: Binding<StopDetailsFilter?>) {
self.globalFetcher = globalFetcher
_scheduleFetcher = StateObject(wrappedValue: ScheduleFetcher(backend: backend))
_predictionsFetcher = StateObject(wrappedValue: PredictionsFetcher(socket: socket))
self.stop = stop
self.filter = filter
_filter = filter
}

var body: some View {
Expand All @@ -46,7 +46,7 @@ struct StopDetailsPage: View {
schedules: scheduleFetcher.schedules,
predictions: predictionsFetcher.predictions,
filterAtTime: now.toKotlinInstant()
), now: now.toKotlinInstant())
), now: now.toKotlinInstant(), filter: $filter)
} else {
ProgressView()
}
Expand Down
Loading

0 comments on commit f7ab56e

Please sign in to comment.