From 5272f0f3285ea6d31d6e6c7d5471033517d69b1c Mon Sep 17 00:00:00 2001 From: HuanCheng65 <22636177+HuanCheng65@users.noreply.github.com> Date: Sat, 23 Sep 2023 16:14:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=95=BF=E6=8C=89=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E5=8D=95=E6=9D=A1=E6=90=9C=E7=B4=A2=E5=8E=86=E5=8F=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tieba/post/ui/page/search/SearchPage.kt | 10 ++++-- .../post/ui/page/search/SearchViewModel.kt | 34 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/search/SearchPage.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/search/SearchPage.kt index b79bcf4d..eaaba2dc 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/search/SearchPage.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/search/SearchPage.kt @@ -6,6 +6,7 @@ import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.PressInteraction import androidx.compose.foundation.layout.Arrangement @@ -271,6 +272,7 @@ fun SearchPage( }, expanded = expanded, onToggleExpand = { expanded = !expanded }, + onDelete = { viewModel.send(SearchUiIntent.DeleteSearchHistory(it.id)) }, onClear = { viewModel.send(SearchUiIntent.ClearSearchHistory) } ) } @@ -389,13 +391,14 @@ private fun ColumnScope.SearchTabRow( } } -@OptIn(ExperimentalLayoutApi::class) +@OptIn(ExperimentalLayoutApi::class, ExperimentalFoundationApi::class) @Composable private fun SearchHistoryList( searchHistories: ImmutableList, onSearchHistoryClick: (SearchHistory) -> Unit, expanded: Boolean = false, onToggleExpand: () -> Unit = {}, + onDelete: (SearchHistory) -> Unit = {}, onClear: () -> Unit = {}, ) { val hasMore = remember(searchHistories) { @@ -438,7 +441,10 @@ private fun SearchHistoryList( modifier = Modifier .padding(bottom = 8.dp) .clip(RoundedCornerShape(100)) - .clickable { onSearchHistoryClick(searchHistory) } + .combinedClickable( + onClick = { onSearchHistoryClick(searchHistory) }, + onLongClick = { onDelete(searchHistory) } + ) .background(ExtendedTheme.colors.chip) .padding(horizontal = 16.dp, vertical = 8.dp) ) { diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/search/SearchViewModel.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/search/SearchViewModel.kt index e7602cdd..4d6e50ac 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/search/SearchViewModel.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/search/SearchViewModel.kt @@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach import org.litepal.LitePal +import org.litepal.extension.delete import org.litepal.extension.deleteAll import org.litepal.extension.find @@ -47,6 +48,10 @@ class SearchViewModel : App.INSTANCE.getString(R.string.toast_clear_failure, partialChange.errorMessage) ) + is SearchPartialChange.DeleteSearchHistory.Failure -> CommonUiEvent.Toast( + App.INSTANCE.getString(R.string.toast_delete_failure, partialChange.errorMessage) + ) + is SearchPartialChange.SubmitKeyword -> SearchUiEvent.KeywordChanged(partialChange.keyword) else -> null @@ -61,6 +66,8 @@ class SearchViewModel : .flatMapConcat { produceInitPartialChange() }, intentFlow.filterIsInstance() .flatMapConcat { produceClearHistoryPartialChange() }, + intentFlow.filterIsInstance() + .flatMapConcat { it.producePartialChange() }, intentFlow.filterIsInstance() .flatMapConcat { it.producePartialChange() }, ) @@ -83,6 +90,14 @@ class SearchViewModel : emit(SearchPartialChange.ClearSearchHistory.Failure(it.getErrorMessage())) }.flowOn(Dispatchers.IO) + private fun SearchUiIntent.DeleteSearchHistory.producePartialChange() = + flow { + LitePal.delete(id) + emit(SearchPartialChange.DeleteSearchHistory.Success(id)) + }.catch { + emit(SearchPartialChange.DeleteSearchHistory.Failure(it.getErrorMessage())) + }.flowOn(Dispatchers.IO) + private fun SearchUiIntent.SubmitKeyword.producePartialChange() = flowOf(SearchPartialChange.SubmitKeyword(keyword.trim())) .onEach { @@ -102,6 +117,8 @@ sealed interface SearchUiIntent : UiIntent { data object ClearSearchHistory : SearchUiIntent + data class DeleteSearchHistory(val id: Long) : SearchUiIntent + data class SubmitKeyword(val keyword: String) : SearchUiIntent } @@ -130,6 +147,23 @@ sealed interface SearchPartialChange : PartialChange { ) : ClearSearchHistory() } + sealed class DeleteSearchHistory : SearchPartialChange { + override fun reduce(oldState: SearchUiState): SearchUiState = when (this) { + is Success -> oldState.copy( + searchHistories = oldState.searchHistories.filterNot { it.id == id } + .toImmutableList() + ) + + is Failure -> oldState + } + + data class Success(val id: Long) : DeleteSearchHistory() + + data class Failure( + val errorMessage: String, + ) : DeleteSearchHistory() + } + data class SubmitKeyword(val keyword: String) : SearchPartialChange { override fun reduce(oldState: SearchUiState): SearchUiState { if (keyword.isEmpty()) {