-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
181 additions
and
168 deletions.
There are no files selected for viewing
168 changes: 1 addition & 167 deletions
168
apps/mobile/metro-now/metro-now Watch App/ContentView.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
145 changes: 145 additions & 0 deletions
145
.../metro-now/metro-now Watch App/pages/closest-stop-list/closest-stop-list-view-model.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
// metro-now | ||
// https://github.com/krystxf/metro-now | ||
|
||
import Alamofire | ||
import CoreLocation | ||
|
||
private let REFETCH_INTERVAL: TimeInterval = 3 // seconds | ||
private let SECONDS_BEFORE: TimeInterval = 3 // how many seconds after departure will it still be visible | ||
|
||
class ClosestStopListViewModel: NSObject, ObservableObject, CLLocationManagerDelegate { | ||
private let locationManager = CLLocationManager() | ||
@Published var location: CLLocation? | ||
|
||
@Published var stops: [ApiStop]? | ||
@Published var closestStop: ApiStop? | ||
@Published var departures: [ApiDeparture]? | ||
|
||
private var refreshTimer: Timer? | ||
|
||
override init() { | ||
super.init() | ||
locationManager.delegate = self | ||
locationManager.requestWhenInUseAuthorization() | ||
locationManager.startUpdatingLocation() | ||
|
||
getStops() | ||
startPeriodicRefresh() | ||
} | ||
|
||
deinit { | ||
stopPeriodicRefresh() | ||
} | ||
|
||
private func startPeriodicRefresh() { | ||
stopPeriodicRefresh() // Stop any existing timer to avoid duplication. | ||
refreshTimer = Timer.scheduledTimer( | ||
withTimeInterval: REFETCH_INTERVAL, | ||
repeats: true | ||
) { [weak self] _ in | ||
|
||
guard let self, let closestStop else { | ||
return | ||
} | ||
|
||
getDepartures(stopsIds: [closestStop.id]) | ||
} | ||
} | ||
|
||
private func stopPeriodicRefresh() { | ||
refreshTimer?.invalidate() | ||
refreshTimer = nil | ||
} | ||
|
||
func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) { | ||
guard let location = locations.last else { | ||
return | ||
} | ||
self.location = location | ||
|
||
updateClosestStop() | ||
} | ||
|
||
func updateClosestStop() { | ||
guard let location else { | ||
return | ||
} | ||
|
||
if | ||
let stops, | ||
let nextValue = findClosestStop(to: location, stops: stops), | ||
nextValue.id != self.closestStop?.id | ||
{ | ||
closestStop = nextValue | ||
getDepartures(stopsIds: [nextValue.id]) | ||
} | ||
} | ||
|
||
private func getStops() { | ||
let request = AF.request( | ||
"\(ENDPOINT)/v1/stop/all", | ||
method: .get, | ||
parameters: ["metroOnly": String(true)] | ||
) | ||
|
||
request | ||
.validate() | ||
.responseDecodable(of: [ApiStop].self) { response in | ||
switch response.result { | ||
case let .success(fetchedStops): | ||
DispatchQueue.main.async { | ||
self.stops = fetchedStops | ||
print("Fetched \(fetchedStops.count) metro stops") | ||
|
||
self.updateClosestStop() | ||
} | ||
case let .failure(error): | ||
print("Error fetching metroStops: \(error)") | ||
} | ||
} | ||
} | ||
|
||
private func getDepartures(stopsIds: [String]) { | ||
let decoder = JSONDecoder() | ||
decoder.dateDecodingStrategy = .iso8601 | ||
|
||
let request = AF.request( | ||
"\(ENDPOINT)/v2/departure", | ||
method: .get, | ||
parameters: [ | ||
"stop": stopsIds, | ||
"limit": 20, | ||
"totalLimit": 80, | ||
"minutesBefore": 1, | ||
] | ||
) | ||
|
||
request | ||
.validate() | ||
.responseDecodable(of: [ApiDeparture].self, decoder: decoder) { response in | ||
switch response.result { | ||
case let .success(fetchedDepartures): | ||
DispatchQueue.main.async { | ||
if let oldDepartures = self.departures { | ||
self.departures = uniqueBy( | ||
array: oldDepartures + fetchedDepartures, | ||
by: \.id | ||
) | ||
.filter { | ||
$0.departure.predicted > Date.now - SECONDS_BEFORE | ||
} | ||
.sorted(by: { | ||
$0.departure.scheduled < $1.departure.scheduled | ||
}) | ||
} else { | ||
self.departures = fetchedDepartures | ||
} | ||
|
||
print("Fetched \(fetchedDepartures.count) departures") | ||
} | ||
case let .failure(error): | ||
print("Error fetching stops: \(error)") | ||
} | ||
} | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
...mobile/metro-now/metro-now Watch App/pages/closest-stop-list/closest-stop-list.view.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// metro-now | ||
// https://github.com/krystxf/metro-now | ||
|
||
|
||
import SwiftUI | ||
|
||
struct ClosestStopListView: View { | ||
@StateObject private var viewModel = ClosestStopListViewModel() | ||
|
||
var body: some View { | ||
VStack { | ||
if let closestStop = viewModel.closestStop { | ||
let platforms = closestStop.platforms.filter { $0.routes.count > 0 } | ||
|
||
StopDeparturesView( | ||
title: closestStop.name, | ||
platforms: platforms.map { platform in | ||
let metroLine = MetroLine(rawValue: platform.routes[0].name) | ||
|
||
return MainPagePlatform( | ||
id: platform.id, | ||
metroLine: metroLine, | ||
departures: viewModel.departures?.filter { departure in | ||
departure.platformId == platform.id | ||
} | ||
) | ||
} | ||
) | ||
} else { | ||
ProgressView() | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters