From 949ae6f3a77438bd209e59f37b337bb69974603b Mon Sep 17 00:00:00 2001 From: Karan Sharma <55722391+ksharma-xyz@users.noreply.github.com> Date: Sat, 26 Oct 2024 23:00:55 +1100 Subject: [PATCH] UI: Refactor SavedTripsScreen with LaunchedEffect and add LoadSavedTrips event (#260) ### TL;DR Added a LoadSavedTrips event and improved the SavedTrips screen initialization. ### What changed? - Added a new `LoadSavedTrips` event to `SavedTripUiEvent` sealed interface. - Replaced the `isActive` state flow in `SavedTripsViewModel` with a direct call to `loadSavedTrips()` using `LaunchedEffect` in `SavedTripsDestination`. - Removed the `ANR_TIMEOUT` constant and related code from `SavedTripsViewModel`. - Added a spacer at the bottom of the `TimeTableScreen` to improve layout. ### Why make this change? This change simplifies the initialization process for the SavedTrips screen by using a more direct approach to load saved trips. It removes unnecessary complexity related to the `isActive` state flow and improves the overall user experience by ensuring saved trips are loaded immediately upon screen entry. The added spacer in the TimeTable screen enhances the layout and prevents content from being obscured by system bars or other UI elements. https://github.com/user-attachments/assets/c664863c-3096-446e-87fe-28a7e2c5a066 --- .../ui/state/savedtrip/SavedTripUiEvent.kt | 1 + .../ui/savedtrips/SavedTripsDestination.kt | 6 ++++-- .../ui/savedtrips/SavedTripsViewModel.kt | 18 +----------------- .../planner/ui/timetable/TimeTableScreen.kt | 11 +++++++++++ 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/feature/trip-planner/state/src/main/kotlin/xyz/ksharma/krail/trip/planner/ui/state/savedtrip/SavedTripUiEvent.kt b/feature/trip-planner/state/src/main/kotlin/xyz/ksharma/krail/trip/planner/ui/state/savedtrip/SavedTripUiEvent.kt index a05379e7..b77289b0 100644 --- a/feature/trip-planner/state/src/main/kotlin/xyz/ksharma/krail/trip/planner/ui/state/savedtrip/SavedTripUiEvent.kt +++ b/feature/trip-planner/state/src/main/kotlin/xyz/ksharma/krail/trip/planner/ui/state/savedtrip/SavedTripUiEvent.kt @@ -3,6 +3,7 @@ package xyz.ksharma.krail.trip.planner.ui.state.savedtrip import xyz.ksharma.krail.trip.planner.ui.state.timetable.Trip sealed interface SavedTripUiEvent { + data object LoadSavedTrips : SavedTripUiEvent data class SavedTripClicked(val trip: Trip) : SavedTripUiEvent data class DeleteSavedTrip(val trip: Trip) : SavedTripUiEvent } diff --git a/feature/trip-planner/ui/src/main/kotlin/xyz/ksharma/krail/trip/planner/ui/savedtrips/SavedTripsDestination.kt b/feature/trip-planner/ui/src/main/kotlin/xyz/ksharma/krail/trip/planner/ui/savedtrips/SavedTripsDestination.kt index e937b1ac..5e2482b6 100644 --- a/feature/trip-planner/ui/src/main/kotlin/xyz/ksharma/krail/trip/planner/ui/savedtrips/SavedTripsDestination.kt +++ b/feature/trip-planner/ui/src/main/kotlin/xyz/ksharma/krail/trip/planner/ui/savedtrips/SavedTripsDestination.kt @@ -15,6 +15,7 @@ import xyz.ksharma.krail.trip.planner.ui.navigation.SavedTripsRoute import xyz.ksharma.krail.trip.planner.ui.navigation.SearchStopFieldType import xyz.ksharma.krail.trip.planner.ui.navigation.SearchStopRoute import xyz.ksharma.krail.trip.planner.ui.navigation.TimeTableRoute +import xyz.ksharma.krail.trip.planner.ui.state.savedtrip.SavedTripUiEvent import xyz.ksharma.krail.trip.planner.ui.state.searchstop.model.StopItem import xyz.ksharma.krail.trip.planner.ui.state.searchstop.model.StopItem.Companion.fromJsonString @@ -29,8 +30,9 @@ internal fun NavGraphBuilder.savedTripsDestination(navController: NavHostControl val toArg: String? = backStackEntry.savedStateHandle.get(SearchStopFieldType.TO.key) - // Subscribe to the isActive state flow to Load Trips only once at the start. - val isActive by viewModel.isActive.collectAsStateWithLifecycle() + LaunchedEffect(Unit) { + viewModel.onEvent(SavedTripUiEvent.LoadSavedTrips) + } // Cannot use 'rememberSaveable' here because StopItem is not Parcelable. // But it's saved in backStackEntry.savedStateHandle as json, so it's able to diff --git a/feature/trip-planner/ui/src/main/kotlin/xyz/ksharma/krail/trip/planner/ui/savedtrips/SavedTripsViewModel.kt b/feature/trip-planner/ui/src/main/kotlin/xyz/ksharma/krail/trip/planner/ui/savedtrips/SavedTripsViewModel.kt index 66a1dd3b..2aee2227 100644 --- a/feature/trip-planner/ui/src/main/kotlin/xyz/ksharma/krail/trip/planner/ui/savedtrips/SavedTripsViewModel.kt +++ b/feature/trip-planner/ui/src/main/kotlin/xyz/ksharma/krail/trip/planner/ui/savedtrips/SavedTripsViewModel.kt @@ -6,10 +6,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import timber.log.Timber @@ -21,7 +18,6 @@ import xyz.ksharma.krail.trip.planner.ui.state.savedtrip.SavedTripUiEvent import xyz.ksharma.krail.trip.planner.ui.state.savedtrip.SavedTripsState import xyz.ksharma.krail.trip.planner.ui.state.timetable.Trip import javax.inject.Inject -import kotlin.time.Duration.Companion.seconds @HiltViewModel class SavedTripsViewModel @Inject constructor( @@ -34,15 +30,6 @@ class SavedTripsViewModel @Inject constructor( private val _uiState: MutableStateFlow = MutableStateFlow(SavedTripsState()) val uiState: StateFlow = _uiState - private val _isActive: MutableStateFlow = MutableStateFlow(false) - val isActive: StateFlow = _isActive.onStart { - loadSavedTrips() - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(ANR_TIMEOUT.inWholeMilliseconds), - initialValue = true, - ) - private fun loadSavedTrips() { viewModelScope.launch(context = ioDispatcher) { val trips = sandook.keys().mapNotNull { key -> @@ -61,6 +48,7 @@ class SavedTripsViewModel @Inject constructor( when (event) { is SavedTripUiEvent.DeleteSavedTrip -> onDeleteSavedTrip(event.trip) is SavedTripUiEvent.SavedTripClicked -> onSavedTripClicked(event.trip) + SavedTripUiEvent.LoadSavedTrips -> loadSavedTrips() } } @@ -79,8 +67,4 @@ class SavedTripsViewModel @Inject constructor( private fun updateUiState(block: SavedTripsState.() -> SavedTripsState) { _uiState.update(block) } - - companion object { - private val ANR_TIMEOUT = 5.seconds - } } diff --git a/feature/trip-planner/ui/src/main/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableScreen.kt b/feature/trip-planner/ui/src/main/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableScreen.kt index f4077459..6ea377f9 100644 --- a/feature/trip-planner/ui/src/main/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableScreen.kt +++ b/feature/trip-planner/ui/src/main/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableScreen.kt @@ -4,8 +4,11 @@ import androidx.compose.animation.animateContentSize import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable @@ -91,6 +94,14 @@ fun TimeTableScreen( Text("No data found") } } + + item { + Spacer( + modifier = Modifier + .height(96.dp) + .systemBarsPadding(), + ) + } } }