@@ -7,14 +7,20 @@ import com.cornellappdev.transit.networking.EcosystemNetworkApi
77import com.cornellappdev.transit.networking.RoutesNetworkApi
88import com.cornellappdev.transit.util.ECOSYSTEM_FLAG
99import com.google.android.gms.maps.model.LatLng
10- import kotlinx.coroutines.CancellationException
1110import kotlinx.coroutines.CoroutineScope
1211import kotlinx.coroutines.Dispatchers
1312import kotlinx.coroutines.flow.MutableStateFlow
1413import kotlinx.coroutines.flow.asStateFlow
1514import kotlinx.coroutines.launch
15+ import java.util.concurrent.atomic.AtomicLong
1616import javax.inject.Inject
1717import javax.inject.Singleton
18+ import kotlin.coroutines.cancellation.CancellationException
19+
20+ data class PlaceSearchState (
21+ val query : String ,
22+ val response : ApiResponse <List <Place >>,
23+ )
1824
1925/* *
2026 * Repository for data related to routes
@@ -52,6 +58,9 @@ class RouteRepository @Inject constructor(
5258 private val _placeFlow : MutableStateFlow <ApiResponse <List <Place >>> =
5359 MutableStateFlow (ApiResponse .Pending )
5460
61+ private val _placeSearchStateFlow : MutableStateFlow <PlaceSearchState > =
62+ MutableStateFlow (PlaceSearchState (query = " " , response = ApiResponse .Success (emptyList())))
63+
5564 private val _lastRouteFlow : MutableStateFlow <ApiResponse <RouteOptions >> =
5665 MutableStateFlow (ApiResponse .Pending )
5766
@@ -61,6 +70,9 @@ class RouteRepository @Inject constructor(
6170 private val _libraryFlow : MutableStateFlow <ApiResponse <List <Library >>> =
6271 MutableStateFlow (ApiResponse .Pending )
6372
73+ // Monotonic token used to ensure only the latest search request updates placeFlow.
74+ private val latestSearchToken = AtomicLong (0L )
75+
6476 init {
6577 fetchAllStops()
6678 if (ECOSYSTEM_FLAG ) {
@@ -84,6 +96,11 @@ class RouteRepository @Inject constructor(
8496 */
8597 val placeFlow = _placeFlow .asStateFlow()
8698
99+ /* *
100+ * A StateFlow holding the last queried location tagged by query.
101+ */
102+ val placeSearchStateFlow = _placeSearchStateFlow .asStateFlow()
103+
87104 /* *
88105 * A StateFlow holding the list of all printers
89106 */
@@ -94,6 +111,13 @@ class RouteRepository @Inject constructor(
94111 */
95112 val libraryFlow = _libraryFlow .asStateFlow()
96113
114+ /* *
115+ * Clears the currently displayed route result.
116+ */
117+ fun clearLastRoute () {
118+ _lastRouteFlow .value = ApiResponse .Idle
119+ }
120+
97121 /* *
98122 * Makes a new call to backend for all stops.
99123 */
@@ -143,15 +167,47 @@ class RouteRepository @Inject constructor(
143167 * Makes a new call to places related to a query string.
144168 */
145169 fun makeSearch (query : String ) {
146- _placeFlow .value = ApiResponse .Pending
170+ val normalizedQuery = query.trim()
171+ val token = latestSearchToken.incrementAndGet()
172+
173+ if (normalizedQuery.isBlank()) {
174+ if (token == latestSearchToken.get()) {
175+ _placeFlow .value = ApiResponse .Success (emptyList())
176+ _placeSearchStateFlow .value =
177+ PlaceSearchState (query = " " , response = ApiResponse .Success (emptyList()))
178+ }
179+ return
180+ }
181+
182+ _placeSearchStateFlow .value = PlaceSearchState (
183+ query = normalizedQuery,
184+ response = ApiResponse .Pending
185+ )
147186 CoroutineScope (Dispatchers .IO ).launch {
148187 try {
149- val placeResponse = appleSearch(SearchQuery (query))
188+ if (token == latestSearchToken.get()){
189+ _placeFlow .value = ApiResponse .Pending
190+ }
191+ val placeResponse = appleSearch(SearchQuery (normalizedQuery))
150192 val res = placeResponse.unwrap()
151193 val totalLocations = (res.places ? : emptyList()) + (res.stops ? : (emptyList()))
152- _placeFlow .value = ApiResponse .Success (totalLocations)
194+ if (token == latestSearchToken.get()) {
195+ _placeFlow .value = ApiResponse .Success (totalLocations)
196+ _placeSearchStateFlow .value = PlaceSearchState (
197+ query = normalizedQuery,
198+ response = ApiResponse .Success (totalLocations)
199+ )
200+ }
201+ } catch (_: CancellationException ) {
202+ // Ignore cancellation; latest query owns the flow update.
153203 } catch (e: Exception ) {
154- _placeFlow .value = ApiResponse .Error
204+ if (token == latestSearchToken.get()) {
205+ _placeFlow .value = ApiResponse .Error
206+ _placeSearchStateFlow .value = PlaceSearchState (
207+ query = normalizedQuery,
208+ response = ApiResponse .Error
209+ )
210+ }
155211 }
156212 }
157213 }
@@ -194,5 +250,4 @@ class RouteRepository @Inject constructor(
194250 _lastRouteFlow .value = ApiResponse .Error
195251 }
196252 }
197-
198253}
0 commit comments