Skip to content

Commit

Permalink
fix(subscribeToPredictions): periodically check predictions stale
Browse files Browse the repository at this point in the history
  • Loading branch information
KaylaBrady committed Jan 10, 2025
1 parent 1daa39c commit b437f8c
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ import com.mbta.tid.mbta_app.repositories.MockPredictionsRepository
import com.mbta.tid.mbta_app.repositories.MockSettingsRepository
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.test.runTest
import kotlinx.datetime.Clock
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
Expand Down Expand Up @@ -161,4 +163,38 @@ class SubscribeToPredictionsTest {
assertNotNull(predictions)
assertTrue(connected)
}

@Test
fun testCheckPredictionsStaleCalled() = runTest {
val objects = ObjectCollectionBuilder()
objects.prediction()
val predictionsOnJoin = PredictionsByStopJoinResponse(objects)
val predictionsRepo = MockPredictionsRepository({}, {}, {}, null, predictionsOnJoin)

predictionsRepo.lastUpdated = Clock.System.now()

var stopIds = mutableStateOf(listOf("place-a"))

var checkPredictionsStaleCount = 0
val mockErrorRepo =
MockErrorBannerStateRepository(
onCheckPredictionsStale = { checkPredictionsStaleCount += 1 }
)

composeTestRule.setContent {
var stopIds by remember { stopIds }
subscribeToPredictions(
stopIds,
predictionsRepo,
ErrorBannerViewModel(
false,
mockErrorRepo,
MockSettingsRepository(),
),
1.seconds
)
}

composeTestRule.waitUntil(timeoutMillis = 3000) { checkPredictionsStaleCount >= 2 }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@ package com.mbta.tid.mbta_app.android.state

import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.compose.LifecycleResumeEffect
import androidx.lifecycle.viewmodel.compose.viewModel
import com.mbta.tid.mbta_app.android.component.ErrorBannerViewModel
import com.mbta.tid.mbta_app.android.util.timer
import com.mbta.tid.mbta_app.model.response.ApiResult
import com.mbta.tid.mbta_app.model.response.PredictionsByStopJoinResponse
import com.mbta.tid.mbta_app.model.response.PredictionsByStopMessageResponse
import com.mbta.tid.mbta_app.model.response.PredictionsStreamDataResponse
import com.mbta.tid.mbta_app.repositories.IPredictionsRepository
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -92,15 +95,17 @@ class PredictionsViewModel(
}

fun checkPredictionsStale() {
predictionsRepository.lastUpdated?.let { lastPredictions ->
errorBannerViewModel.errorRepository.checkPredictionsStale(
predictionsLastUpdated = lastPredictions,
predictionQuantity = predictions.value?.predictionQuantity() ?: 0,
action = {
disconnect()
connect(currentStopIds)
}
)
CoroutineScope(Dispatchers.IO).launch {
predictionsRepository.lastUpdated?.let { lastPredictions ->
errorBannerViewModel.errorRepository.checkPredictionsStale(
predictionsLastUpdated = lastPredictions,
predictionQuantity = predictions.value?.predictionQuantity() ?: 0,
action = {
disconnect()
connect(currentStopIds)
}
)
}
}
}

Expand All @@ -118,13 +123,16 @@ class PredictionsViewModel(
fun subscribeToPredictions(
stopIds: List<String>?,
predictionsRepository: IPredictionsRepository = koinInject(),
errorBannerViewModel: ErrorBannerViewModel
errorBannerViewModel: ErrorBannerViewModel,
checkPredictionsStaleInterval: Duration = 5.seconds
): PredictionsStreamDataResponse? {
val viewModel: PredictionsViewModel =
viewModel(
factory = PredictionsViewModel.Factory(predictionsRepository, errorBannerViewModel)
)

val timer = timer(checkPredictionsStaleInterval)

LifecycleResumeEffect(key1 = stopIds) {
CoroutineScope(Dispatchers.IO).launch {
viewModel.checkPredictionsStale()
Expand All @@ -133,5 +141,8 @@ fun subscribeToPredictions(

onPauseOrDispose { viewModel.disconnect() }
}

LaunchedEffect(key1 = timer) { viewModel.checkPredictionsStale() }

return viewModel.predictionsFlow.collectAsState(initial = null).value
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ abstract class IErrorBannerStateRepository(initialState: ErrorBannerState? = nul
}
}

fun checkPredictionsStale(
open fun checkPredictionsStale(
predictionsLastUpdated: Instant,
predictionQuantity: Int,
action: () -> Unit
Expand Down Expand Up @@ -93,12 +93,23 @@ class ErrorBannerStateRepository : IErrorBannerStateRepository(), KoinComponent
class MockErrorBannerStateRepository(
state: ErrorBannerState? = null,
onSubscribeToNetworkChanges: (() -> Unit)? = null,
onCheckPredictionsStale: (() -> Unit)? = null
) : IErrorBannerStateRepository(state) {
private val onSubscribeToNetworkChanges = onSubscribeToNetworkChanges
private val onCheckPredictionsStale = onCheckPredictionsStale

val mutableFlow
get() = flow

override fun subscribeToNetworkStatusChanges() {
onSubscribeToNetworkChanges?.invoke()
}

override fun checkPredictionsStale(
predictionsLastUpdated: Instant,
predictionQuantity: Int,
action: () -> Unit
) {
onCheckPredictionsStale?.invoke()
}
}

0 comments on commit b437f8c

Please sign in to comment.