diff --git a/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureViewModel.kt b/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureViewModel.kt index 65990365..210e9432 100644 --- a/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureViewModel.kt +++ b/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureViewModel.kt @@ -33,6 +33,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch @HiltViewModel @@ -93,17 +94,21 @@ class ProcedureViewModel @Inject constructor( val currentState = _uiState.value if (currentState is UiState.Success) { - _uiState.value = currentState.copy( - data = currentState.data.copy(worries = worriesToUse) - ) + _uiState.update { state -> + (state as? UiState.Success)?.copy( + data = state.data.copy(worries = worriesToUse) + ) ?: state + } } else { - _uiState.value = UiState.Success( - ProcedureUiState( - worries = worriesToUse, - startDay = startDateArg, - screenHeightDp = latestScreenHeightDp + _uiState.update { + UiState.Success( + ProcedureUiState( + worries = worriesToUse, + startDay = startDateArg, + screenHeightDp = latestScreenHeightDp + ) ) - ) + } } } .onLogFailure { } @@ -269,21 +274,21 @@ class ProcedureViewModel @Inject constructor( } fun onNextClick() { - var queryToFetch: ProceduresQuery? = null + val current = currentStateOrNull() ?: return - _uiState.updateSuccess { current -> - if (!current.isNextEnabled) return@updateSuccess current + if (!current.isNextEnabled) return - if (current.flow == ProcedureFlow.Entry) { - val selected = current.existenceSelectedIndex ?: return@updateSuccess current - val nextFlow = if (selected == 1) ProcedureFlow.NoTreat else ProcedureFlow.Treat - val nextStep = if (nextFlow == ProcedureFlow.NoTreat) { - ProcedureStep.Category - } else { - ProcedureStep.RecoverySchedule - } + if (current.flow == ProcedureFlow.Entry) { + val selected = current.existenceSelectedIndex ?: return + val nextFlow = if (selected == 1) ProcedureFlow.NoTreat else ProcedureFlow.Treat + val nextStep = if (nextFlow == ProcedureFlow.NoTreat) { + ProcedureStep.Category + } else { + ProcedureStep.RecoverySchedule + } - return@updateSuccess current.copy( + _uiState.updateSuccess { + it.copy( flow = nextFlow, step = nextStep, selectedWorryId = null, @@ -297,60 +302,117 @@ class ProcedureViewModel @Inject constructor( procedureDowntimeMap = emptyMap() ) } + return + } - if (current.step == ProcedureStep.Filtering || - current.step == ProcedureStep.FilteringWithSearch - ) { - val nextStep = current.nextStep() + if (current.step == ProcedureStep.Filtering || + current.step == ProcedureStep.FilteringWithSearch + ) { + val nextStep = current.nextStep() - val updatedProcedureItems = current.procedureItems.map { item -> - if (item.id in current.selectedProcedureCardIds) { - item.copy(displayMode = ProcedureCardDisplayMode.Selectable) - } else { - item - } - }.toImmutableList() + val updatedProcedureItems = current.procedureItems.map { item -> + if (item.id in current.selectedProcedureCardIds) { + item.copy(displayMode = ProcedureCardDisplayMode.Selectable) + } else { + item.copy(displayMode = ProcedureCardDisplayMode.Basic) + } + }.toImmutableList() - val updatedSelectedItems = current.selectedProcedureItems.map { item -> - if (item.id in current.selectedProcedureCardIds) { - item.copy(displayMode = ProcedureCardDisplayMode.Selectable) - } else { - item - } - }.toImmutableList() + val updatedSelectedItems = current.selectedProcedureItems.map { item -> + if (item.id in current.selectedProcedureCardIds) { + item.copy(displayMode = ProcedureCardDisplayMode.Selectable) + } else { + item.copy(displayMode = ProcedureCardDisplayMode.Basic) + } + }.toImmutableList() - return@updateSuccess current.copy( + _uiState.updateSuccess { + it.copy( step = nextStep, procedureItems = updatedProcedureItems, selectedProcedureItems = updatedSelectedItems ) } + return + } - val nextStep = current.nextStep() + val nextStep = current.nextStep() - if (current.flow == ProcedureFlow.NoTreat && - current.step == ProcedureStep.RecoverySchedule - ) { - queryToFetch = ProceduresQuery( + if (current.flow == ProcedureFlow.NoTreat && + current.step == ProcedureStep.RecoverySchedule + ) { + _uiState.update { UiState.Loading } + + viewModelScope.launch { + procedureRepository.getProcedures( keyword = null, worryId = current.selectedWorryId - ) + ).onSuccess { response -> + val items = response.procedures + .map { it.toUiModel() } + .toPersistentList() + + _uiState.update { + UiState.Success( + current.copy( + step = nextStep, + procedureItems = items + ) + ) + } + }.onLogFailure { + _uiState.update { UiState.Success(current) } + } } + return + } + + if (current.flow == ProcedureFlow.Treat && + current.step == ProcedureStep.RecoverySchedule + ) { + _uiState.update { UiState.Loading } - if (current.flow == ProcedureFlow.Treat && - current.step == ProcedureStep.RecoverySchedule - ) { + viewModelScope.launch { val keyword = current.searchQuery.trim().takeIf { it.isNotEmpty() } - queryToFetch = ProceduresQuery( + procedureRepository.getProcedures( keyword = keyword, worryId = null - ) - } + ).onSuccess { response -> + val normalizedKeyword = keyword?.trim().takeIf { !it.isNullOrEmpty() } + val filteredProcedures = if (normalizedKeyword == null) { + response.procedures + } else { + response.procedures.filter { procedure -> + procedure.name.contains(normalizedKeyword, ignoreCase = true) || + ( + procedure.category?.contains( + normalizedKeyword, + ignoreCase = true + ) == true + ) + } + } - current.copy(step = nextStep) + val items = filteredProcedures + .map { it.toUiModel() } + .toPersistentList() + + _uiState.update { + UiState.Success( + current.copy( + step = nextStep, + procedureItems = items + ) + ) + } + }.onLogFailure { + _uiState.update { UiState.Success(current) } + } + } + return } - queryToFetch?.let { fetchProcedures(keyword = it.keyword, worryId = it.worryId) } + _uiState.updateSuccess { it.copy(step = nextStep) } } fun onBackClick() { @@ -499,11 +561,6 @@ class ProcedureViewModel @Inject constructor( } } -private data class ProceduresQuery( - val keyword: String?, - val worryId: Long? -) - private fun ProcedureModel.toUiModel(): ProcedureCardItemUiModel = ProcedureCardItemUiModel( id = this.id, diff --git a/app/src/main/java/com/cherrish/android/presentation/main/MainScreen.kt b/app/src/main/java/com/cherrish/android/presentation/main/MainScreen.kt index 2449615a..593eaa25 100644 --- a/app/src/main/java/com/cherrish/android/presentation/main/MainScreen.kt +++ b/app/src/main/java/com/cherrish/android/presentation/main/MainScreen.kt @@ -1,12 +1,17 @@ package com.cherrish.android.presentation.main +import androidx.activity.compose.BackHandler +import androidx.activity.compose.LocalActivity import androidx.compose.animation.EnterTransition import androidx.compose.animation.ExitTransition import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableLongStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.compose.NavHost import com.cherrish.android.data.repository.ChallengeMissionProgressRepository @@ -31,6 +36,8 @@ fun MainScreen( val currentTab by appState.currentTab.collectAsStateWithLifecycle() val coroutineScope = rememberCoroutineScope() + HandleBackPressToExit() + CompositionLocalProvider(LocalCalendarEventBus provides appState.calendarEventBus) { Scaffold( bottomBar = { @@ -115,3 +122,19 @@ fun MainScreen( } } } + +@Composable +private fun HandleBackPressToExit( + enabled: Boolean = true, + backPressInterval: Long = 2000L +) { + val context = LocalActivity.current + var backPressedTime by remember { mutableLongStateOf(0L) } + + BackHandler(enabled = enabled) { + if (System.currentTimeMillis() - backPressedTime <= backPressInterval) { + context?.finish() + } + backPressedTime = System.currentTimeMillis() + } +}