From 55fb068ca167321a8b84ea61e22d33e2541fa8c2 Mon Sep 17 00:00:00 2001 From: Kim Nahyeon Date: Fri, 23 Jan 2026 18:53:48 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[FEAT/#155]=20=EB=92=A4=EB=A1=9C=EA=B0=80?= =?UTF-8?q?=EA=B8=B0=202=EB=B2=88=20=ED=81=B4=EB=A6=AD=20=EC=8B=9C=20?= =?UTF-8?q?=EC=95=B1=20=EC=A2=85=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/presentation/main/MainScreen.kt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) 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() + } +} From bd33f09db586fdbff5330f94c822228c2bf81365 Mon Sep 17 00:00:00 2001 From: Kim Nahyeon Date: Fri, 23 Jan 2026 19:13:00 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[REF/#155]=20=EC=8B=9C=EC=88=A0=20=ED=94=8C?= =?UTF-8?q?=EB=A1=9C=EC=9A=B0=20=EB=A1=9C=EB=94=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calendar/procedure/ProcedureViewModel.kt | 175 ++++++++++++------ 1 file changed, 116 insertions(+), 59 deletions(-) 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..4a43f9d7 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 + ) { + _uiState.update { UiState.Loading } - if (current.flow == ProcedureFlow.NoTreat && - current.step == ProcedureStep.RecoverySchedule - ) { - queryToFetch = ProceduresQuery( + 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 + ) + } + } + + val items = filteredProcedures + .map { it.toUiModel() } + .toPersistentList() - current.copy(step = nextStep) + _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, From a57369ffe7d263551b83aa281f686fedd791d882 Mon Sep 17 00:00:00 2001 From: Kim Nahyeon Date: Fri, 23 Jan 2026 19:19:01 +0900 Subject: [PATCH 3/3] [CHORE/#155] Lint format --- .../calendar/procedure/ProcedureViewModel.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 4a43f9d7..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 @@ -384,12 +384,12 @@ class ProcedureViewModel @Inject constructor( } else { response.procedures.filter { procedure -> procedure.name.contains(normalizedKeyword, ignoreCase = true) || - ( - procedure.category?.contains( - normalizedKeyword, - ignoreCase = true - ) == true - ) + ( + procedure.category?.contains( + normalizedKeyword, + ignoreCase = true + ) == true + ) } }