diff --git a/app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureSearchTextField.kt b/app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureSearchTextField.kt index 72edebf8..0e362eec 100644 --- a/app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureSearchTextField.kt +++ b/app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureSearchTextField.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.cherrish.android.R +import com.cherrish.android.core.common.extension.noRippleClickable import com.cherrish.android.core.designsystem.theme.CherrishTheme @Composable @@ -78,7 +79,12 @@ fun ProcedureTextField( imageVector = ImageVector.vectorResource(id = R.drawable.ic_search), contentDescription = null, tint = CherrishTheme.colors.gray500, - modifier = Modifier.align(Alignment.CenterEnd) + modifier = Modifier + .align(Alignment.CenterEnd) + .noRippleClickable { + onSearchAction() + keyboardController?.hide() + } ) } } diff --git a/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureScreen.kt b/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureScreen.kt index 506d37d3..2a6911c5 100644 --- a/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureScreen.kt +++ b/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureScreen.kt @@ -231,6 +231,7 @@ fun ProcedureScreen( onCardClick = onProcedureCardClick, onSearchAction = onSearchAction, query = uiState.searchQuery, + searchedQuery = uiState.searchedQuery, onQueryChange = onSearchableQueryChange, bottomContentPadding = uiState.lazyColumnBottomPadding, modifier = Modifier diff --git a/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureUiState.kt b/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureUiState.kt index 9c267884..33c4e8df 100644 --- a/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureUiState.kt +++ b/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureUiState.kt @@ -43,6 +43,7 @@ data class ProcedureUiState( val selectedWorryName: String = "", val searchQuery: String = "", + val searchedQuery: String = "", val showDowntimeBottomSheet: Boolean = false, val selectedProcedureForDowntime: ProcedureCardItemUiModel? = null, 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 15c3cc22..65990365 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 @@ -121,6 +121,7 @@ class ProcedureViewModel @Inject constructor( fun onSearchAction(query: String) { val current = currentStateOrNull() ?: return val keyword = query.trim().takeIf { it.isNotEmpty() } + _uiState.updateSuccess { it.copy(searchedQuery = query.trim()) } fetchProcedures( keyword = keyword, worryId = current.selectedWorryId, @@ -433,7 +434,22 @@ class ProcedureViewModel @Inject constructor( viewModelScope.launch { procedureRepository.getProcedures(keyword = keyword, worryId = worryId) .onSuccess { response -> - val items = response.procedures + 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() @@ -511,7 +527,9 @@ private fun ProcedureUiState.toEntryState(): ProcedureUiState { selectedDowntime = null, selectedProcedureCardIds = persistentListOf(), selectedProcedureItems = persistentListOf(), - procedureDowntimeMap = emptyMap() + procedureDowntimeMap = emptyMap(), + searchQuery = "", + searchedQuery = "" ) } diff --git a/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/content/FilteringWithSearchContent.kt b/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/content/FilteringWithSearchContent.kt index 13bb0131..287202cf 100644 --- a/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/content/FilteringWithSearchContent.kt +++ b/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/content/FilteringWithSearchContent.kt @@ -33,6 +33,7 @@ import com.cherrish.android.presentation.calendar.procedure.model.ProcedureCardD import com.cherrish.android.presentation.calendar.procedure.model.ProcedureCardItemUiModel import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList /* TODO: 삭제 예정 */ private val mockProcedureCardItems = persistentListOf( @@ -101,10 +102,21 @@ fun FilteringWithSearchContent( onCardClick: (Long) -> Unit, onSearchAction: (String) -> Unit, query: String, + searchedQuery: String, onQueryChange: (String) -> Unit, bottomContentPadding: Dp, modifier: Modifier = Modifier ) { + val normalizedQuery = searchedQuery.trim() + val filteredItems = if (normalizedQuery.isEmpty()) { + cardItems + } else { + cardItems.filter { item -> + item.name.contains(normalizedQuery, ignoreCase = true) || + item.category.contains(normalizedQuery, ignoreCase = true) + }.toPersistentList() + } + Column( modifier = modifier.fillMaxWidth() ) { @@ -128,7 +140,7 @@ fun FilteringWithSearchContent( Spacer(modifier = Modifier.height(10.dp)) } - if (cardItems.isEmpty()) { + if (filteredItems.isEmpty()) { item { EmptySearchResult( modifier = Modifier.fillParentMaxSize() @@ -136,7 +148,7 @@ fun FilteringWithSearchContent( } } else { items( - items = cardItems, + items = filteredItems, key = { it.id } ) { item -> ProcedureCard( @@ -202,6 +214,7 @@ private fun FilteringWithSearchContentPreview() { }, onSearchAction = {}, query = query, + searchedQuery = "", bottomContentPadding = 20.dp, onQueryChange = { query = it } ) @@ -218,6 +231,7 @@ private fun FilteringWithSearchContentEmptyPreview() { onCardClick = {}, onSearchAction = {}, query = "레이저 토닝", + searchedQuery = "레이저 토닝", bottomContentPadding = 20.dp, onQueryChange = {} ) diff --git a/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/content/RecoveryScheduleContent.kt b/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/content/RecoveryScheduleContent.kt index ff85a102..0ceecf3f 100644 --- a/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/content/RecoveryScheduleContent.kt +++ b/app/src/main/java/com/cherrish/android/presentation/calendar/procedure/content/RecoveryScheduleContent.kt @@ -19,7 +19,11 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.input.pointer.PointerEventPass +import androidx.compose.ui.input.pointer.changedToUp +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign @@ -45,8 +49,23 @@ fun RecoveryScheduleContent( ) { val hasSelection = selectedIndex != null && selectedIndex >= 0 val focusManager = LocalFocusManager.current - - Column(modifier = modifier) { + val keyboardController = LocalSoftwareKeyboardController.current + + Column( + modifier = modifier.pointerInput(focusManager) { + awaitPointerEventScope { + while (true) { + val event = awaitPointerEvent(PointerEventPass.Final) + val isTapUp = event.changes.any { it.changedToUp() } + val isConsumed = event.changes.any { it.isConsumed } + if (isTapUp && !isConsumed) { + focusManager.clearFocus() + keyboardController?.hide() + } + } + } + } + ) { SelectionSection( title = "회복을 계획할 때 고려해야 할\n중요한 일정이 있나요?", items = persistentListOf("아직 없어요", "네, 있어요"),