feat: 长按删除单条搜索历史
This commit is contained in:
parent
4e6d9aab70
commit
5272f0f328
|
|
@ -6,6 +6,7 @@ import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.interaction.PressInteraction
|
import androidx.compose.foundation.interaction.PressInteraction
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
|
@ -271,6 +272,7 @@ fun SearchPage(
|
||||||
},
|
},
|
||||||
expanded = expanded,
|
expanded = expanded,
|
||||||
onToggleExpand = { expanded = !expanded },
|
onToggleExpand = { expanded = !expanded },
|
||||||
|
onDelete = { viewModel.send(SearchUiIntent.DeleteSearchHistory(it.id)) },
|
||||||
onClear = { viewModel.send(SearchUiIntent.ClearSearchHistory) }
|
onClear = { viewModel.send(SearchUiIntent.ClearSearchHistory) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -389,13 +391,14 @@ private fun ColumnScope.SearchTabRow(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
@OptIn(ExperimentalLayoutApi::class, ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun SearchHistoryList(
|
private fun SearchHistoryList(
|
||||||
searchHistories: ImmutableList<SearchHistory>,
|
searchHistories: ImmutableList<SearchHistory>,
|
||||||
onSearchHistoryClick: (SearchHistory) -> Unit,
|
onSearchHistoryClick: (SearchHistory) -> Unit,
|
||||||
expanded: Boolean = false,
|
expanded: Boolean = false,
|
||||||
onToggleExpand: () -> Unit = {},
|
onToggleExpand: () -> Unit = {},
|
||||||
|
onDelete: (SearchHistory) -> Unit = {},
|
||||||
onClear: () -> Unit = {},
|
onClear: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
val hasMore = remember(searchHistories) {
|
val hasMore = remember(searchHistories) {
|
||||||
|
|
@ -438,7 +441,10 @@ private fun SearchHistoryList(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(bottom = 8.dp)
|
.padding(bottom = 8.dp)
|
||||||
.clip(RoundedCornerShape(100))
|
.clip(RoundedCornerShape(100))
|
||||||
.clickable { onSearchHistoryClick(searchHistory) }
|
.combinedClickable(
|
||||||
|
onClick = { onSearchHistoryClick(searchHistory) },
|
||||||
|
onLongClick = { onDelete(searchHistory) }
|
||||||
|
)
|
||||||
.background(ExtendedTheme.colors.chip)
|
.background(ExtendedTheme.colors.chip)
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.merge
|
import kotlinx.coroutines.flow.merge
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import org.litepal.LitePal
|
import org.litepal.LitePal
|
||||||
|
import org.litepal.extension.delete
|
||||||
import org.litepal.extension.deleteAll
|
import org.litepal.extension.deleteAll
|
||||||
import org.litepal.extension.find
|
import org.litepal.extension.find
|
||||||
|
|
||||||
|
|
@ -47,6 +48,10 @@ class SearchViewModel :
|
||||||
App.INSTANCE.getString(R.string.toast_clear_failure, partialChange.errorMessage)
|
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)
|
is SearchPartialChange.SubmitKeyword -> SearchUiEvent.KeywordChanged(partialChange.keyword)
|
||||||
|
|
||||||
else -> null
|
else -> null
|
||||||
|
|
@ -61,6 +66,8 @@ class SearchViewModel :
|
||||||
.flatMapConcat { produceInitPartialChange() },
|
.flatMapConcat { produceInitPartialChange() },
|
||||||
intentFlow.filterIsInstance<SearchUiIntent.ClearSearchHistory>()
|
intentFlow.filterIsInstance<SearchUiIntent.ClearSearchHistory>()
|
||||||
.flatMapConcat { produceClearHistoryPartialChange() },
|
.flatMapConcat { produceClearHistoryPartialChange() },
|
||||||
|
intentFlow.filterIsInstance<SearchUiIntent.DeleteSearchHistory>()
|
||||||
|
.flatMapConcat { it.producePartialChange() },
|
||||||
intentFlow.filterIsInstance<SearchUiIntent.SubmitKeyword>()
|
intentFlow.filterIsInstance<SearchUiIntent.SubmitKeyword>()
|
||||||
.flatMapConcat { it.producePartialChange() },
|
.flatMapConcat { it.producePartialChange() },
|
||||||
)
|
)
|
||||||
|
|
@ -83,6 +90,14 @@ class SearchViewModel :
|
||||||
emit(SearchPartialChange.ClearSearchHistory.Failure(it.getErrorMessage()))
|
emit(SearchPartialChange.ClearSearchHistory.Failure(it.getErrorMessage()))
|
||||||
}.flowOn(Dispatchers.IO)
|
}.flowOn(Dispatchers.IO)
|
||||||
|
|
||||||
|
private fun SearchUiIntent.DeleteSearchHistory.producePartialChange() =
|
||||||
|
flow<SearchPartialChange.DeleteSearchHistory> {
|
||||||
|
LitePal.delete<SearchHistory>(id)
|
||||||
|
emit(SearchPartialChange.DeleteSearchHistory.Success(id))
|
||||||
|
}.catch {
|
||||||
|
emit(SearchPartialChange.DeleteSearchHistory.Failure(it.getErrorMessage()))
|
||||||
|
}.flowOn(Dispatchers.IO)
|
||||||
|
|
||||||
private fun SearchUiIntent.SubmitKeyword.producePartialChange() =
|
private fun SearchUiIntent.SubmitKeyword.producePartialChange() =
|
||||||
flowOf(SearchPartialChange.SubmitKeyword(keyword.trim()))
|
flowOf(SearchPartialChange.SubmitKeyword(keyword.trim()))
|
||||||
.onEach {
|
.onEach {
|
||||||
|
|
@ -102,6 +117,8 @@ sealed interface SearchUiIntent : UiIntent {
|
||||||
|
|
||||||
data object ClearSearchHistory : SearchUiIntent
|
data object ClearSearchHistory : SearchUiIntent
|
||||||
|
|
||||||
|
data class DeleteSearchHistory(val id: Long) : SearchUiIntent
|
||||||
|
|
||||||
data class SubmitKeyword(val keyword: String) : SearchUiIntent
|
data class SubmitKeyword(val keyword: String) : SearchUiIntent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,6 +147,23 @@ sealed interface SearchPartialChange : PartialChange<SearchUiState> {
|
||||||
) : ClearSearchHistory()
|
) : 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 {
|
data class SubmitKeyword(val keyword: String) : SearchPartialChange {
|
||||||
override fun reduce(oldState: SearchUiState): SearchUiState {
|
override fun reduce(oldState: SearchUiState): SearchUiState {
|
||||||
if (keyword.isEmpty()) {
|
if (keyword.isEmpty()) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue