pref: 交换清除全部和展开更多历史按钮

This commit is contained in:
HuanCheng65 2023-09-23 15:55:46 +08:00
parent 5f7203228e
commit 4e6d9aab70
No known key found for this signature in database
GPG Key ID: 5EC9DD60A32C7360
4 changed files with 87 additions and 53 deletions

View File

@ -6,28 +6,23 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
internal val DefaultTextStyle = TextStyle.Default.copy()
// Set of Material typography styles to start with // Set of Material typography styles to start with
val Typography = Typography( val Typography = Typography(
body1 = TextStyle( body1 = DefaultTextStyle.copy(
fontFamily = FontFamily.Default, fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Normal,
fontSize = 16.sp fontSize = 16.sp
), ),
subtitle1 = TextStyle( subtitle1 = DefaultTextStyle.copy(
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
fontSize = 16.sp, fontSize = 16.sp,
letterSpacing = 0.15.sp letterSpacing = 0.15.sp
), ),
/* Other default text styles to override button = DefaultTextStyle.copy(
button = TextStyle( fontWeight = FontWeight.Medium,
fontFamily = FontFamily.Default, fontSize = 14.sp,
fontWeight = FontWeight.W500, letterSpacing = 0.15.sp
fontSize = 14.sp
),
caption = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 12.sp
) )
*/
) )

View File

@ -38,8 +38,9 @@ import androidx.compose.material.Text
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBack import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material.icons.rounded.ArrowDropDown import androidx.compose.material.icons.rounded.ArrowDropDown
import androidx.compose.material.icons.rounded.CleaningServices
import androidx.compose.material.icons.rounded.Clear import androidx.compose.material.icons.rounded.Clear
import androidx.compose.material.icons.rounded.ExpandLess
import androidx.compose.material.icons.rounded.ExpandMore
import androidx.compose.material.icons.rounded.Search import androidx.compose.material.icons.rounded.Search
import androidx.compose.material.ripple.rememberRipple import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -79,6 +80,7 @@ import com.huanchengfly.tieba.post.models.database.SearchHistory
import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme
import com.huanchengfly.tieba.post.ui.common.theme.compose.TiebaLiteTheme import com.huanchengfly.tieba.post.ui.common.theme.compose.TiebaLiteTheme
import com.huanchengfly.tieba.post.ui.page.ProvideNavigator import com.huanchengfly.tieba.post.ui.page.ProvideNavigator
import com.huanchengfly.tieba.post.ui.page.destinations.SearchPageDestination
import com.huanchengfly.tieba.post.ui.page.search.forum.SearchForumPage import com.huanchengfly.tieba.post.ui.page.search.forum.SearchForumPage
import com.huanchengfly.tieba.post.ui.page.search.thread.SearchThreadPage import com.huanchengfly.tieba.post.ui.page.search.thread.SearchThreadPage
import com.huanchengfly.tieba.post.ui.page.search.thread.SearchThreadSortType import com.huanchengfly.tieba.post.ui.page.search.thread.SearchThreadSortType
@ -88,6 +90,7 @@ import com.huanchengfly.tieba.post.ui.widgets.compose.BaseTextField
import com.huanchengfly.tieba.post.ui.widgets.compose.Button import com.huanchengfly.tieba.post.ui.widgets.compose.Button
import com.huanchengfly.tieba.post.ui.widgets.compose.ClickMenu import com.huanchengfly.tieba.post.ui.widgets.compose.ClickMenu
import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoadHorizontalPager import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoadHorizontalPager
import com.huanchengfly.tieba.post.ui.widgets.compose.MyBackHandler
import com.huanchengfly.tieba.post.ui.widgets.compose.MyScaffold import com.huanchengfly.tieba.post.ui.widgets.compose.MyScaffold
import com.huanchengfly.tieba.post.ui.widgets.compose.PagerTabIndicator import com.huanchengfly.tieba.post.ui.widgets.compose.PagerTabIndicator
import com.huanchengfly.tieba.post.ui.widgets.compose.TopAppBarContainer import com.huanchengfly.tieba.post.ui.widgets.compose.TopAppBarContainer
@ -132,10 +135,25 @@ fun SearchPage(
prop1 = SearchUiState::keyword, prop1 = SearchUiState::keyword,
initial = "" initial = ""
) )
val isKeywordEmpty by remember {
derivedStateOf { keyword.isEmpty() } val isKeywordEmpty by viewModel.uiState.collectPartialAsState(
} prop1 = SearchUiState::isKeywordEmpty,
initial = true
)
var inputKeyword by remember { mutableStateOf("") } var inputKeyword by remember { mutableStateOf("") }
LaunchedEffect(keyword) {
if (keyword.isNotEmpty() && keyword != inputKeyword) {
inputKeyword = keyword
}
}
MyBackHandler(
enabled = !isKeywordEmpty,
currentScreen = SearchPageDestination
) {
viewModel.send(SearchUiIntent.SubmitKeyword(""))
}
var expanded by remember { mutableStateOf(false) } var expanded by remember { mutableStateOf(false) }
val initialSortType = remember { SearchThreadSortType.SORT_TYPE_NEWEST } val initialSortType = remember { SearchThreadSortType.SORT_TYPE_NEWEST }
@ -145,7 +163,7 @@ fun SearchPage(
} }
viewModel.onEvent<SearchUiEvent.KeywordChanged> { viewModel.onEvent<SearchUiEvent.KeywordChanged> {
inputKeyword = it.keyword inputKeyword = it.keyword
emitGlobalEventSuspend(it) if (it.keyword.isNotBlank()) emitGlobalEventSuspend(it)
} }
val pages by remember { val pages by remember {
@ -213,7 +231,13 @@ fun SearchPage(
onKeywordSubmit = { onKeywordSubmit = {
viewModel.send(SearchUiIntent.SubmitKeyword(it)) viewModel.send(SearchUiIntent.SubmitKeyword(it))
}, },
onBack = { navigator.navigateUp() } onBack = {
if (isKeywordEmpty) {
navigator.navigateUp()
} else {
viewModel.send(SearchUiIntent.SubmitKeyword(""))
}
}
) )
} }
}, },
@ -224,27 +248,32 @@ fun SearchPage(
} }
} }
) { ) {
if (!isKeywordEmpty) { Box(modifier = Modifier.fillMaxSize()) {
ProvideNavigator(navigator = navigator) { if (!isKeywordEmpty) {
LazyLoadHorizontalPager( ProvideNavigator(navigator = navigator) {
state = pagerState, LazyLoadHorizontalPager(
key = { pages[it].id } state = pagerState,
) { key = { pages[it].id },
pages[it].content() modifier = Modifier.fillMaxSize(),
) {
pages[it].content()
}
}
} else {
Column(
modifier = Modifier.fillMaxSize()
) {
SearchHistoryList(
searchHistories = searchHistories,
onSearchHistoryClick = {
inputKeyword = it.content
viewModel.send(SearchUiIntent.SubmitKeyword(it.content))
},
expanded = expanded,
onToggleExpand = { expanded = !expanded },
onClear = { viewModel.send(SearchUiIntent.ClearSearchHistory) }
)
} }
}
} else {
Column {
SearchHistoryList(
searchHistories = searchHistories,
onSearchHistoryClick = {
inputKeyword = it.content
viewModel.send(SearchUiIntent.SubmitKeyword(it.content))
},
expanded = expanded,
onToggleExpand = { expanded = !expanded },
onClear = { viewModel.send(SearchUiIntent.ClearSearchHistory) }
)
} }
} }
} }
@ -390,12 +419,10 @@ private fun SearchHistoryList(
.weight(1f), .weight(1f),
style = MaterialTheme.typography.subtitle1 style = MaterialTheme.typography.subtitle1
) )
if (hasMore) { if (hasItem) {
Text( Text(
text = if (!expanded) stringResource(id = R.string.button_expand) else stringResource( text = stringResource(id = R.string.button_clear_all),
id = R.string.button_collapse modifier = Modifier.clickable(onClick = onClear),
),
modifier = Modifier.clickable(onClick = onToggleExpand),
style = MaterialTheme.typography.button style = MaterialTheme.typography.button
) )
} }
@ -421,9 +448,9 @@ private fun SearchHistoryList(
} }
} }
} }
if (hasItem) { if (hasMore) {
Button( Button(
onClick = onClear, onClick = onToggleExpand,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.textButtonColors( colors = ButtonDefaults.textButtonColors(
backgroundColor = Color.Transparent, backgroundColor = Color.Transparent,
@ -435,18 +462,21 @@ private fun SearchHistoryList(
horizontalArrangement = Arrangement.spacedBy(8.dp) horizontalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
Icon( Icon(
imageVector = Icons.Rounded.CleaningServices, imageVector = if (expanded) Icons.Rounded.ExpandLess else Icons.Rounded.ExpandMore,
contentDescription = null, contentDescription = null,
modifier = Modifier.size(16.dp) modifier = Modifier.size(16.dp)
) )
Text( Text(
text = stringResource(id = R.string.title_clear_search_history), text = stringResource(
id = if (expanded) R.string.button_expand_less_history else R.string.button_expand_more_history
),
style = MaterialTheme.typography.button, style = MaterialTheme.typography.button,
fontWeight = FontWeight.Bold fontWeight = FontWeight.Bold
) )
} }
} }
} else { }
if (!hasItem) {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()

View File

@ -84,10 +84,14 @@ class SearchViewModel :
}.flowOn(Dispatchers.IO) }.flowOn(Dispatchers.IO)
private fun SearchUiIntent.SubmitKeyword.producePartialChange() = private fun SearchUiIntent.SubmitKeyword.producePartialChange() =
flowOf(SearchPartialChange.SubmitKeyword(keyword)) flowOf(SearchPartialChange.SubmitKeyword(keyword.trim()))
.onEach { .onEach {
runCatching { runCatching {
SearchHistory(keyword).saveOrUpdate("content = ?", keyword) val trimKeyword = keyword.trim()
if (trimKeyword.isNotBlank()) SearchHistory(trimKeyword).saveOrUpdate(
"content = ?",
trimKeyword
)
} }
} }
} }
@ -119,7 +123,7 @@ sealed interface SearchPartialChange : PartialChange<SearchUiState> {
is Failure -> oldState is Failure -> oldState
} }
object Success : ClearSearchHistory() data object Success : ClearSearchHistory()
data class Failure( data class Failure(
val errorMessage: String, val errorMessage: String,
@ -129,13 +133,14 @@ sealed interface SearchPartialChange : PartialChange<SearchUiState> {
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()) {
return oldState.copy(keyword = keyword) return oldState.copy(isKeywordEmpty = true)
} }
val newSearchHistories = (oldState.searchHistories val newSearchHistories = (oldState.searchHistories
.filterNot { it.content == keyword } + SearchHistory(content = keyword)) .filterNot { it.content == keyword } + SearchHistory(content = keyword))
.sortedByDescending { it.timestamp } .sortedByDescending { it.timestamp }
return oldState.copy( return oldState.copy(
keyword = keyword, keyword = keyword,
isKeywordEmpty = false,
searchHistories = newSearchHistories.toImmutableList() searchHistories = newSearchHistories.toImmutableList()
) )
} }
@ -144,6 +149,7 @@ sealed interface SearchPartialChange : PartialChange<SearchUiState> {
data class SearchUiState( data class SearchUiState(
val keyword: String = "", val keyword: String = "",
val isKeywordEmpty: Boolean = true,
val searchHistories: ImmutableList<SearchHistory> = persistentListOf(), val searchHistories: ImmutableList<SearchHistory> = persistentListOf(),
) : UiState ) : UiState

View File

@ -716,4 +716,7 @@
<string name="toast_clear_failure">清除失败 %s</string> <string name="toast_clear_failure">清除失败 %s</string>
<string name="title_recommend">推荐</string> <string name="title_recommend">推荐</string>
<string name="title_fuzzy_match_user">相关用户</string> <string name="title_fuzzy_match_user">相关用户</string>
<string name="button_expand_more_history">展开历史</string>
<string name="button_expand_less_history">收起历史</string>
<string name="button_clear_all">清除全部</string>
</resources> </resources>