diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt index e45271787..d6a2aa403 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt @@ -63,6 +63,7 @@ import org.ooni.probe.domain.GetSettings import org.ooni.probe.domain.GetStorageUsed import org.ooni.probe.domain.GetTestDescriptors import org.ooni.probe.domain.GetTestDescriptorsBySpec +import org.ooni.probe.domain.MarkJustFinishedTestAsSeen import org.ooni.probe.domain.ObserveAndConfigureAutoRun import org.ooni.probe.domain.RunBackgroundStateManager import org.ooni.probe.domain.RunDescriptors @@ -294,6 +295,9 @@ class Dependencies( private val getTestDescriptorsBySpec by lazy { GetTestDescriptorsBySpec(getTestDescriptors = getTestDescriptors::invoke) } + private val markJustFinishedTestAsSeen by lazy { + MarkJustFinishedTestAsSeen(runBackgroundStateManager::updateState) + } val observeAndConfigureAutoRun by lazy { ObserveAndConfigureAutoRun( backgroundContext = backgroundContext, @@ -475,6 +479,7 @@ class Dependencies( getResults = getResults::invoke, getDescriptors = getTestDescriptors::invoke, deleteAllResults = deleteAllResults::invoke, + markJustFinishedTestAsSeen = markJustFinishedTestAsSeen::invoke, ) fun runningViewModel( diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/MarkJustFinishedTestAsSeen.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/MarkJustFinishedTestAsSeen.kt new file mode 100644 index 000000000..d4af3ec3f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/domain/MarkJustFinishedTestAsSeen.kt @@ -0,0 +1,17 @@ +package org.ooni.probe.domain + +import org.ooni.probe.data.models.RunBackgroundState + +class MarkJustFinishedTestAsSeen( + private val setRunBackgroundState: ((RunBackgroundState) -> RunBackgroundState) -> Unit, +) { + operator fun invoke() { + setRunBackgroundState { state -> + if (state is RunBackgroundState.Idle && state.justFinishedTest) { + state.copy(justFinishedTest = false) + } else { + state + } + } + } +} diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/dashboard/DashboardViewModel.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/dashboard/DashboardViewModel.kt index a3ae76fc0..3063ec47b 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/dashboard/DashboardViewModel.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/dashboard/DashboardViewModel.kt @@ -15,8 +15,8 @@ import org.ooni.probe.data.models.Descriptor import org.ooni.probe.data.models.DescriptorType import org.ooni.probe.data.models.DescriptorUpdatesStatus import org.ooni.probe.data.models.InstalledTestDescriptorModel -import org.ooni.probe.data.models.TestRunError import org.ooni.probe.data.models.RunBackgroundState +import org.ooni.probe.data.models.TestRunError import org.ooni.probe.data.models.UpdateStatusType class DashboardViewModel( @@ -47,14 +47,17 @@ class DashboardViewModel( .onEach { firstRun -> if (firstRun) goToOnboarding() } .launchIn(viewModelScope) - observeAvailableUpdatesState().onEach { updates -> - _state.update { - it.copy( - availableUpdates = updates.availableUpdates.toList(), - refreshType = updates.refreshType, - ) + observeAvailableUpdatesState() + .onEach { updates -> + _state.update { + it.copy( + availableUpdates = updates.availableUpdates.toList(), + refreshType = updates.refreshType, + ) + } } - }.launchIn(viewModelScope) + .launchIn(viewModelScope) + getTestDescriptors() .onEach { tests -> _state.update { it.copy(descriptors = tests.groupByType()) } @@ -97,9 +100,7 @@ class DashboardViewModel( events .filterIsInstance() - .onEach { event -> - goToDescriptor(event.descriptor.key) - } + .onEach { event -> goToDescriptor(event.descriptor.key) } .launchIn(viewModelScope) events @@ -109,30 +110,40 @@ class DashboardViewModel( } .launchIn(viewModelScope) - events.filterIsInstance().onEach { - state.value.descriptors[DescriptorType.Installed] - ?.map { (it.source as Descriptor.Source.Installed).value } - ?.let { descriptors -> - fetchDescriptorUpdate(descriptors) + events + .filterIsInstance() + .onEach { + state.value.descriptors[DescriptorType.Installed] + ?.map { (it.source as Descriptor.Source.Installed).value } + ?.let { descriptors -> + fetchDescriptorUpdate(descriptors) + } + }.launchIn(viewModelScope) + + events + .filterIsInstance() + .onEach { + _state.update { + it.copy( + refreshType = UpdateStatusType.None, + ) } - }.launchIn(viewModelScope) - events.filterIsInstance().onEach { - _state.update { - it.copy( - refreshType = UpdateStatusType.None, - ) + reviewUpdates(state.value.availableUpdates) + goToReviewDescriptorUpdates() } - reviewUpdates(state.value.availableUpdates) - goToReviewDescriptorUpdates() - }.launchIn(viewModelScope) - events.filterIsInstance().onEach { - cancelUpdates(state.value.availableUpdates) - _state.update { - it.copy( - refreshType = UpdateStatusType.None, - ) + .launchIn(viewModelScope) + + events + .filterIsInstance() + .onEach { + cancelUpdates(state.value.availableUpdates) + _state.update { + it.copy( + refreshType = UpdateStatusType.None, + ) + } } - }.launchIn(viewModelScope) + .launchIn(viewModelScope) } fun onEvent(event: Event) { diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/results/ResultsScreen.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/results/ResultsScreen.kt index e893a3ef1..ebe9d25a0 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/results/ResultsScreen.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/results/ResultsScreen.kt @@ -25,6 +25,7 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -175,6 +176,10 @@ fun ResultsScreen( }, ) } + + LaunchedEffect(Unit) { + onEvent(ResultsViewModel.Event.Start) + } } @Composable diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/results/ResultsViewModel.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/results/ResultsViewModel.kt index e3bb36002..38c3487ce 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/results/ResultsViewModel.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/results/ResultsViewModel.kt @@ -26,6 +26,7 @@ class ResultsViewModel( getResults: (ResultFilter) -> Flow>, getDescriptors: () -> Flow>, deleteAllResults: suspend () -> Unit, + markJustFinishedTestAsSeen: () -> Unit, ) : ViewModel() { private val events = MutableSharedFlow(extraBufferCapacity = 1) @@ -61,6 +62,11 @@ class ResultsViewModel( } .launchIn(viewModelScope) + events + .filterIsInstance() + .onEach { markJustFinishedTestAsSeen() } + .launchIn(viewModelScope) + events .filterIsInstance() .onEach { goToResult(it.result.idOrThrow) } @@ -136,6 +142,8 @@ class ResultsViewModel( ) sealed interface Event { + data object Start : Event + data class ResultClick(val result: ResultListItem) : Event data object UploadClick : Event diff --git a/composeApp/src/commonTest/kotlin/org/ooni/probe/ui/results/ResultsScreenTest.kt b/composeApp/src/commonTest/kotlin/org/ooni/probe/ui/results/ResultsScreenTest.kt index fad7da74e..4f1a76998 100644 --- a/composeApp/src/commonTest/kotlin/org/ooni/probe/ui/results/ResultsScreenTest.kt +++ b/composeApp/src/commonTest/kotlin/org/ooni/probe/ui/results/ResultsScreenTest.kt @@ -18,6 +18,20 @@ import kotlin.test.Test import kotlin.test.assertEquals class ResultsScreenTest { + @Test + fun start() = + runComposeUiTest { + val events = mutableListOf() + setContent { + ResultsScreen( + state = ResultsViewModel.State(results = emptyMap(), isLoading = true), + onEvent = events::add, + ) + } + + assertEquals(ResultsViewModel.Event.Start, events.last()) + } + @Test fun showResults() = runComposeUiTest { @@ -65,7 +79,7 @@ class ResultsScreenTest { } onNodeWithText(title!!).performClick() - assertEquals(ResultsViewModel.Event.ResultClick(item), events.first()) + assertEquals(ResultsViewModel.Event.ResultClick(item), events.last()) } @Test @@ -88,7 +102,7 @@ class ResultsScreenTest { runTest { onNodeWithContentDescription(getString(Res.string.Modal_Delete)).performClick() onNodeWithText(getString(Res.string.Modal_Delete)).performClick() - assertEquals(ResultsViewModel.Event.DeleteAllClick, events.first()) + assertEquals(ResultsViewModel.Event.DeleteAllClick, events.last()) } }