Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Special behavior for target stop on combined stop/trip details #581

Merged
merged 6 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions iosApp/iosApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@
9A7F12132CCB185D0042B0F1 /* TabLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7F12122CCB185D0042B0F1 /* TabLabel.swift */; };
9A7F12172CCFEFAA0042B0F1 /* MoreLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7F12162CCFEFAA0042B0F1 /* MoreLink.swift */; };
9A7F12192CCFF2D20042B0F1 /* MorePhone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7F12182CCFF2D20042B0F1 /* MorePhone.swift */; };
9A8375EE2D0A14DD00E3694F /* TripVehicleCardTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8375ED2D0A14DD00E3694F /* TripVehicleCardTests.swift */; };
9A84DB102D03A6BF00A78C64 /* TripStopsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A84DB0F2D03A6BF00A78C64 /* TripStopsTests.swift */; };
9A887D572B683103006F5B80 /* SearchResultsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A887D562B683103006F5B80 /* SearchResultsContainer.swift */; };
9A887D592B698EF1006F5B80 /* SearchResultViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A887D582B698EF1006F5B80 /* SearchResultViewTests.swift */; };
Expand Down Expand Up @@ -473,6 +474,7 @@
9A7F12122CCB185D0042B0F1 /* TabLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabLabel.swift; sourceTree = "<group>"; };
9A7F12162CCFEFAA0042B0F1 /* MoreLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoreLink.swift; sourceTree = "<group>"; };
9A7F12182CCFF2D20042B0F1 /* MorePhone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MorePhone.swift; sourceTree = "<group>"; };
9A8375ED2D0A14DD00E3694F /* TripVehicleCardTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TripVehicleCardTests.swift; sourceTree = "<group>"; };
9A84DB0F2D03A6BF00A78C64 /* TripStopsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TripStopsTests.swift; sourceTree = "<group>"; };
9A887D562B683103006F5B80 /* SearchResultsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultsContainer.swift; sourceTree = "<group>"; };
9A887D582B698EF1006F5B80 /* SearchResultViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultViewTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -961,6 +963,7 @@
9A9B9FFF2D03565800BCB2BD /* TripDetailsViewTests.swift */,
9A4092EA2D0258A20026EB01 /* TripStopRowTests.swift */,
9A84DB0F2D03A6BF00A78C64 /* TripStopsTests.swift */,
9A8375ED2D0A14DD00E3694F /* TripVehicleCardTests.swift */,
);
path = StopDetails;
sourceTree = "<group>";
Expand Down Expand Up @@ -1479,6 +1482,7 @@
ED5C93F62C4A1AD70086D017 /* TripDetailsHeaderTests.swift in Sources */,
9A9BA0002D03565800BCB2BD /* TripDetailsViewTests.swift in Sources */,
9A60E8E72B8501BD008A8D5C /* RoutePillTests.swift in Sources */,
9A8375EE2D0A14DD00E3694F /* TripVehicleCardTests.swift in Sources */,
6E35D4D32B72CD3900A2BF95 /* HomeMapViewTests.swift in Sources */,
9AF0937A2BD962FF001DF39F /* DirectionPickerTests.swift in Sources */,
9A6FA0282BC72F110067769C /* LegacyStopDetailsPageTests.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion iosApp/iosApp/ComponentViews/UpcomingTripView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
case let .some(prediction):
switch onEnum(of: prediction) {
case let .overridden(overridden):
Text(overridden.textWithLocale()).realtime()
Text(overridden.textWithLocale()).realtime(hideIndicator: hideRealtimeIndicators)
case .hidden, .skipped:
// should have been filtered out already
Text(verbatim: "")
Expand Down Expand Up @@ -147,7 +147,7 @@
case .serviceEndedToday:
Text(
"Service ended",
comment: "The status label for a route and direction when service was running earlier, but no more trips are running today"

Check notice on line 150 in iosApp/iosApp/ComponentViews/UpcomingTripView.swift

View check run for this annotation

Xcode Cloud / MBTA Transit Staging | PR Branch Workflow | iosAppRetries - iOS

iosApp/iosApp/ComponentViews/UpcomingTripView.swift#L150

Line Length Violation: Line should be 120 characters or less; currently it has 139 characters (line_length)
).font(Typography.footnote)
case .noSchedulesToday:
Text(
Expand Down
41 changes: 41 additions & 0 deletions iosApp/iosApp/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -8721,6 +8721,47 @@
}
}
},
"Waiting to depart" : {
"comment" : "Label for a vehicle stopped at a terminal station waiting to start a trip. For example: Waiting to depart Alewife",
"localizations" : {
"es" : {
"stringUnit" : {
"state" : "needs_review",
"value" : "Esperando para partir"
}
},
"ht" : {
"stringUnit" : {
"state" : "needs_review",
"value" : "Ap tann pou yo pati"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "needs_review",
"value" : "Esperando para partir"
}
},
"vi" : {
"stringUnit" : {
"state" : "needs_review",
"value" : "Đang chờ khởi hành"
}
},
"zh-Hans-CN" : {
"stringUnit" : {
"state" : "needs_review",
"value" : "等待出发"
}
},
"zh-Hant-TW" : {
"stringUnit" : {
"state" : "needs_review",
"value" : "等待離開"
}
}
}
},
"We use your location to show you nearby transit options." : {
"localizations" : {
"es" : {
Expand Down
72 changes: 44 additions & 28 deletions iosApp/iosApp/Pages/StopDetails/TripDetailsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,35 @@
self.analytics = analytics
}

func getParentFor(_ stopId: String?, stops: [String: Stop]) -> Stop? {
if let stopId { stops[stopId]?.resolveParent(stops: stops) } else { nil }
}

var body: some View {
content
.task { stopDetailsVM.handleTripFilterChange(tripFilter) }
.onDisappear {
if stopDetailsVM.tripData?.tripFilter == tripFilter {
stopDetailsVM.clearTripDetails()
}
clearMapVehicle()
}
.onChange(of: tripFilter) { nextTripFilter in stopDetailsVM.handleTripFilterChange(nextTripFilter) }
.onChange(of: stopDetailsVM.tripData?.vehicle) { vehicle in mapVM.selectedVehicle = vehicle }
.onReceive(inspection.notice) { inspection.visit(self, $0) }
.withScenePhaseHandlers(
onActive: {
stopDetailsVM.returnFromBackground()
if let tripFilter {
stopDetailsVM.joinTripChannels(tripFilter: tripFilter)
}
},
onInactive: stopDetailsVM.leaveTripChannels,
onBackground: stopDetailsVM.leaveTripChannels
)
}

@ViewBuilder private var content: some View {
VStack(spacing: 16) {
if nearbyVM.showDebugMessages {
DebugView {
Expand All @@ -71,50 +99,33 @@
alertsData: nearbyVM.alerts,
globalData: global
) {
let vehicleStop: Stop? = if let stopId = vehicle.stopId, let allStops = stopDetailsVM.global?.stops {
allStops[stopId]?.resolveParent(stops: allStops)
} else {
nil
}
let vehicleStop: Stop? = getParentFor(vehicle.stopId, stops: global.stops)

let terminalStop: Stop? = getParentFor(tripData.trip.stopIds?.first, stops: global.stops)
let atTerminal = terminalStop != nil && terminalStop?.id == vehicleStop?.id
&& vehicle.currentStatus == .stoppedAt
let terminalEntry = atTerminal ? stops.terminalStop : nil

let routeAccents = stopDetailsVM.getTripRouteAccents()
tripDetails(tripFilter.tripId, stops, vehicle, vehicleStop, routeAccents)
tripDetails(tripFilter.tripId, stops, vehicle, vehicleStop, terminalEntry, routeAccents)
} else {
loadingBody()
}
}
.padding(.horizontal, 6)
.task { stopDetailsVM.handleTripFilterChange(tripFilter) }
.onDisappear {
if stopDetailsVM.tripData?.tripFilter == tripFilter {
stopDetailsVM.clearTripDetails()
}
clearMapVehicle()
}
.onChange(of: tripFilter) { nextTripFilter in stopDetailsVM.handleTripFilterChange(nextTripFilter) }
.onChange(of: stopDetailsVM.tripData?.vehicle) { vehicle in mapVM.selectedVehicle = vehicle }
.onReceive(inspection.notice) { inspection.visit(self, $0) }
.withScenePhaseHandlers(
onActive: {
stopDetailsVM.returnFromBackground()
if let tripFilter {
stopDetailsVM.joinTripChannels(tripFilter: tripFilter)
}
},
onInactive: stopDetailsVM.leaveTripChannels,
onBackground: stopDetailsVM.leaveTripChannels
)
}

@ViewBuilder private func tripDetails(

Check notice on line 118 in iosApp/iosApp/Pages/StopDetails/TripDetailsView.swift

View check run for this annotation

Xcode Cloud / MBTA Transit Staging | PR Branch Workflow | iosAppRetries - iOS

iosApp/iosApp/Pages/StopDetails/TripDetailsView.swift#L118

Function Parameter Count Violation: Function should have 5 parameters or less: it currently has 6 (function_parameter_count)
_ tripId: String,
_ stops: TripDetailsStopList,
_ vehicle: Vehicle?,
_ vehicleStop: Stop?,
_ terminalEntry: TripDetailsStopList.Entry?,
_ routeAccents: TripRouteAccents
) -> some View {
let vehicleShown = vehicle != nil && vehicleStop != nil
VStack(spacing: 0) {
vehicleCardView(vehicle, vehicleStop, tripId, routeAccents).zIndex(1)
vehicleCardView(vehicle, vehicleStop, tripId, terminalEntry, routeAccents).zIndex(1)
TripStops(
targetId: stopId,
stops: stops,
Expand All @@ -134,14 +145,18 @@
_ vehicle: Vehicle?,
_ vehicleStop: Stop?,
_ tripId: String,
_ terminalEntry: TripDetailsStopList.Entry?,
_ routeAccents: TripRouteAccents
) -> some View {
if let vehicle, let vehicleStop {
TripVehicleCard(
vehicle: vehicle,
stop: vehicleStop,
tripId: tripId,
routeAccents: routeAccents
targetId: stopId,
terminalEntry: terminalEntry,
routeAccents: routeAccents,
now: now
)
}
}
Expand All @@ -153,6 +168,7 @@
placeholderInfo.stops,
placeholderInfo.vehicle,
placeholderInfo.vehicleStop,
nil,
TripRouteAccents()
).loadingPlaceholder()
}
Expand Down
104 changes: 77 additions & 27 deletions iosApp/iosApp/Pages/StopDetails/TripVehicleCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
let vehicle: Vehicle
let stop: Stop
let tripId: String
let targetId: String
let terminalEntry: TripDetailsStopList.Entry?
let routeAccents: TripRouteAccents
let now: Date

var body: some View {
ZStack(alignment: .leading) {
Expand All @@ -31,7 +34,7 @@
liveIndicator
}
.frame(maxWidth: .infinity, minHeight: 56, alignment: .leading)
.padding(.vertical, 12)
.padding(.vertical, 8)
.padding(.leading, 30)
.padding(.trailing, 16)
}
Expand Down Expand Up @@ -92,7 +95,13 @@
"Next stop",
comment: "Label for a vehicle's next stop. For example: Next stop Alewife"
)
case .stoppedAt: NSLocalizedString(
case .stoppedAt: terminalEntry != nil ? NSLocalizedString(

Check notice on line 98 in iosApp/iosApp/Pages/StopDetails/TripVehicleCard.swift

View check run for this annotation

Xcode Cloud / MBTA Transit Staging | PR Branch Workflow | iosAppRetries - iOS

iosApp/iosApp/Pages/StopDetails/TripVehicleCard.swift#L98

Void Function in Ternary Violation: Using ternary to call Void functions should be avoided (void_function_in_ternary)
"Waiting to depart",
comment: """
Label for a vehicle stopped at a terminal station waiting to start a trip.
For example: Waiting to depart Alewife
"""
) : NSLocalizedString(
"Now at",
comment: "Label for a where a vehicle is currently stopped. For example: Now at Alewife"
)
Expand All @@ -114,28 +123,59 @@
.rotationEffect(.degrees(225))
routeIcon(routeAccents.type)
.resizable()
.frame(width: 27, height: 27)
.frame(width: 27.5, height: 27.5)
.foregroundColor(routeAccents.textColor)
.overlay {
if targetId == stop.id, vehicle.currentStatus == .stoppedAt {
Image(.stopPinIndicator)
.resizable()
.scaledToFit()
.frame(width: 20, height: 26)
.padding(.bottom, 36)
}
}
}
.accessibilityHidden(true)
.padding([.bottom], 6)
}

var liveIndicator: some View {
HStack {
Image(.liveData)
.resizable()
.frame(width: 16, height: 16)
Text("Live", comment: "Indicates that data is being updated in real-time")
.font(Typography.footnote)
VStack {
HStack {
Image(.liveData)
.resizable()
.frame(width: 16, height: 16)
Text("Live", comment: "Indicates that data is being updated in real-time")
.font(Typography.footnote)
}
.opacity(0.6)
.accessibilityElement()
.accessibilityAddTraits(.isHeader)
.accessibilityLabel(Text(
"Real-time arrivals updating live",
comment: "VoiceOver label for real-time indicator icon"
))
if let upcomingTripViewState {
UpcomingTripView(
prediction: upcomingTripViewState,
routeType: routeAccents.type,
hideRealtimeIndicators: true
).foregroundStyle(Color.text).opacity(0.6)
}
}
}

var upcomingTripViewState: UpcomingTripView.State? {
guard let terminalEntry else { return nil }
if let alert = terminalEntry.alert {
return .noService(alert.effect)
} else {
let formatted = terminalEntry.format(now: now.toKotlinInstant(), routeType: routeAccents.type)
return switch onEnum(of: formatted) {
case .hidden, .skipped: nil
default: .some(formatted)
}
}
.opacity(0.6)
.accessibilityElement()
.accessibilityAddTraits(.isHeader)
.accessibilityLabel(Text(
"Real-time arrivals updating live",
comment: "VoiceOver label for real-time indicator icon"
))
}
}

Expand All @@ -153,23 +193,33 @@
trip.id = "1234"
trip.headsign = "Alewife"
}
let vehicle = Vehicle(id: "y1234", bearing: nil,
currentStatus: __Bridge__Vehicle_CurrentStatus.inTransitTo,
currentStopSequence: 30,
directionId: 1,
latitude: 0.0,
longitude: 0.0,
updatedAt: Date.now.addingTimeInterval(-10).toKotlinInstant(),
routeId: "66",
stopId: "place-davis",
tripId: trip.id)
let vehicle = Vehicle(
id: "y1234", bearing: nil,
currentStatus: __Bridge__Vehicle_CurrentStatus.inTransitTo,
currentStopSequence: 30,
directionId: 1,
latitude: 0.0,
longitude: 0.0,
updatedAt: Date.now.addingTimeInterval(-10).toKotlinInstant(),
routeId: "66",
stopId: "place-davis",
tripId: trip.id
)

let stop = objects.stop { stop in
stop.name = "Davis"
}

List {
TripVehicleCard(vehicle: vehicle, stop: stop, tripId: trip.id, routeAccents: TripRouteAccents(route: red))
TripVehicleCard(
vehicle: vehicle,
stop: stop,
tripId: trip.id,
targetId: "",
terminalEntry: nil,
routeAccents: TripRouteAccents(route: red),
now: Date.now
)
}
.previewDisplayName("VehicleCard")
}
Expand Down
3 changes: 2 additions & 1 deletion iosApp/iosApp/ViewModels/StopDetailsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// Copyright © 2024 MBTA. All rights reserved.
//

// swiftlint:disable type_body_length

Check notice on line 9 in iosApp/iosApp/ViewModels/StopDetailsViewModel.swift

View check run for this annotation

Xcode Cloud / MBTA Transit Staging | PR Branch Workflow | iosAppRetries - iOS

iosApp/iosApp/ViewModels/StopDetailsViewModel.swift#L9

Blanket Disable Command Violation: Use 'next', 'this' or 'previous' instead to disable the 'type_body_length' rule once, or re-enable it as soon as possible` (blanket_disable_command)

import shared
import SwiftPhoenixClient
Expand Down Expand Up @@ -236,7 +236,7 @@
leaveVehicle()
guard let vehicleId = tripFilter.vehicleId else {
// If the filter has a null vehicle ID, we can't join anything, clear the vehicle and return
tripData?.vehicle = nil
Task { @MainActor in tripData?.vehicle = nil }
return
}
let errorKey = "TripDetailsPage.joinVehicle"
Expand Down Expand Up @@ -347,6 +347,7 @@
}
}

@MainActor
func returnFromBackground() {
if let predictionsByStop,
predictionsRepository
Expand Down
Loading
Loading