feat: 新版搜索状态屏
This commit is contained in:
parent
23f73f2e1a
commit
5f7203228e
|
|
@ -72,6 +72,8 @@ import androidx.compose.ui.util.fastForEachIndexed
|
||||||
import com.huanchengfly.tieba.post.R
|
import com.huanchengfly.tieba.post.R
|
||||||
import com.huanchengfly.tieba.post.arch.collectPartialAsState
|
import com.huanchengfly.tieba.post.arch.collectPartialAsState
|
||||||
import com.huanchengfly.tieba.post.arch.emitGlobalEvent
|
import com.huanchengfly.tieba.post.arch.emitGlobalEvent
|
||||||
|
import com.huanchengfly.tieba.post.arch.emitGlobalEventSuspend
|
||||||
|
import com.huanchengfly.tieba.post.arch.onEvent
|
||||||
import com.huanchengfly.tieba.post.arch.pageViewModel
|
import com.huanchengfly.tieba.post.arch.pageViewModel
|
||||||
import com.huanchengfly.tieba.post.models.database.SearchHistory
|
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
|
||||||
|
|
@ -134,12 +136,17 @@ fun SearchPage(
|
||||||
derivedStateOf { keyword.isEmpty() }
|
derivedStateOf { keyword.isEmpty() }
|
||||||
}
|
}
|
||||||
var inputKeyword by remember { mutableStateOf("") }
|
var inputKeyword by remember { mutableStateOf("") }
|
||||||
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val initialSortType = remember { SearchThreadSortType.SORT_TYPE_NEWEST }
|
val initialSortType = remember { SearchThreadSortType.SORT_TYPE_NEWEST }
|
||||||
var searchThreadSortType by remember { mutableIntStateOf(initialSortType) }
|
var searchThreadSortType by remember { mutableIntStateOf(initialSortType) }
|
||||||
LaunchedEffect(searchThreadSortType) {
|
LaunchedEffect(searchThreadSortType) {
|
||||||
emitGlobalEvent(SearchThreadUiEvent.SwitchSortType(searchThreadSortType))
|
emitGlobalEvent(SearchThreadUiEvent.SwitchSortType(searchThreadSortType))
|
||||||
}
|
}
|
||||||
|
viewModel.onEvent<SearchUiEvent.KeywordChanged> {
|
||||||
|
inputKeyword = it.keyword
|
||||||
|
emitGlobalEventSuspend(it)
|
||||||
|
}
|
||||||
|
|
||||||
val pages by remember {
|
val pages by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
|
|
@ -234,6 +241,8 @@ fun SearchPage(
|
||||||
inputKeyword = it.content
|
inputKeyword = it.content
|
||||||
viewModel.send(SearchUiIntent.SubmitKeyword(it.content))
|
viewModel.send(SearchUiIntent.SubmitKeyword(it.content))
|
||||||
},
|
},
|
||||||
|
expanded = expanded,
|
||||||
|
onToggleExpand = { expanded = !expanded },
|
||||||
onClear = { viewModel.send(SearchUiIntent.ClearSearchHistory) }
|
onClear = { viewModel.send(SearchUiIntent.ClearSearchHistory) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,8 @@ class SearchViewModel :
|
||||||
App.INSTANCE.getString(R.string.toast_clear_failure, partialChange.errorMessage)
|
App.INSTANCE.getString(R.string.toast_clear_failure, partialChange.errorMessage)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
is SearchPartialChange.SubmitKeyword -> SearchUiEvent.KeywordChanged(partialChange.keyword)
|
||||||
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,4 +147,6 @@ data class SearchUiState(
|
||||||
val searchHistories: ImmutableList<SearchHistory> = persistentListOf(),
|
val searchHistories: ImmutableList<SearchHistory> = persistentListOf(),
|
||||||
) : UiState
|
) : UiState
|
||||||
|
|
||||||
sealed interface SearchUiEvent : UiEvent
|
sealed interface SearchUiEvent : UiEvent {
|
||||||
|
data class KeywordChanged(val keyword: String) : SearchUiEvent
|
||||||
|
}
|
||||||
|
|
@ -30,16 +30,20 @@ import androidx.compose.ui.unit.dp
|
||||||
import com.huanchengfly.tieba.post.R
|
import com.huanchengfly.tieba.post.R
|
||||||
import com.huanchengfly.tieba.post.api.models.SearchForumBean
|
import com.huanchengfly.tieba.post.api.models.SearchForumBean
|
||||||
import com.huanchengfly.tieba.post.arch.collectPartialAsState
|
import com.huanchengfly.tieba.post.arch.collectPartialAsState
|
||||||
|
import com.huanchengfly.tieba.post.arch.onGlobalEvent
|
||||||
import com.huanchengfly.tieba.post.arch.pageViewModel
|
import com.huanchengfly.tieba.post.arch.pageViewModel
|
||||||
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.pullRefreshIndicator
|
import com.huanchengfly.tieba.post.ui.common.theme.compose.pullRefreshIndicator
|
||||||
import com.huanchengfly.tieba.post.ui.page.LocalNavigator
|
import com.huanchengfly.tieba.post.ui.page.LocalNavigator
|
||||||
import com.huanchengfly.tieba.post.ui.page.destinations.ForumPageDestination
|
import com.huanchengfly.tieba.post.ui.page.destinations.ForumPageDestination
|
||||||
|
import com.huanchengfly.tieba.post.ui.page.search.SearchUiEvent
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.Chip
|
import com.huanchengfly.tieba.post.ui.widgets.Chip
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.Avatar
|
import com.huanchengfly.tieba.post.ui.widgets.compose.Avatar
|
||||||
|
import com.huanchengfly.tieba.post.ui.widgets.compose.ErrorScreen
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoad
|
import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoad
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.LocalShouldLoad
|
import com.huanchengfly.tieba.post.ui.widgets.compose.LocalShouldLoad
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.Sizes
|
import com.huanchengfly.tieba.post.ui.widgets.compose.Sizes
|
||||||
|
import com.huanchengfly.tieba.post.ui.widgets.compose.states.StateScreen
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
|
||||||
|
|
@ -53,20 +57,17 @@ fun SearchForumPage(
|
||||||
viewModel.send(SearchForumUiIntent.Refresh(keyword))
|
viewModel.send(SearchForumUiIntent.Refresh(keyword))
|
||||||
viewModel.initialized = true
|
viewModel.initialized = true
|
||||||
}
|
}
|
||||||
|
val currentKeyword by viewModel.uiState.collectPartialAsState(
|
||||||
val shouldLoad = LocalShouldLoad.current
|
prop1 = SearchForumUiState::keyword,
|
||||||
LaunchedEffect(keyword) {
|
initial = ""
|
||||||
if (viewModel.initialized) {
|
)
|
||||||
if (shouldLoad) {
|
|
||||||
viewModel.send(SearchForumUiIntent.Refresh(keyword))
|
|
||||||
} else {
|
|
||||||
viewModel.initialized = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val isRefreshing by viewModel.uiState.collectPartialAsState(
|
val isRefreshing by viewModel.uiState.collectPartialAsState(
|
||||||
prop1 = SearchForumUiState::isRefreshing,
|
prop1 = SearchForumUiState::isRefreshing,
|
||||||
initial = false
|
initial = true
|
||||||
|
)
|
||||||
|
val error by viewModel.uiState.collectPartialAsState(
|
||||||
|
prop1 = SearchForumUiState::error,
|
||||||
|
initial = null
|
||||||
)
|
)
|
||||||
val exactMatchForum by viewModel.uiState.collectPartialAsState(
|
val exactMatchForum by viewModel.uiState.collectPartialAsState(
|
||||||
prop1 = SearchForumUiState::exactMatchForum,
|
prop1 = SearchForumUiState::exactMatchForum,
|
||||||
|
|
@ -89,6 +90,36 @@ fun SearchForumPage(
|
||||||
onRefresh = { viewModel.send(SearchForumUiIntent.Refresh(keyword)) }
|
onRefresh = { viewModel.send(SearchForumUiIntent.Refresh(keyword)) }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val isEmpty by remember {
|
||||||
|
derivedStateOf { !showExactMatchResult && !showFuzzyMatchResult }
|
||||||
|
}
|
||||||
|
|
||||||
|
onGlobalEvent<SearchUiEvent.KeywordChanged> {
|
||||||
|
viewModel.send(SearchForumUiIntent.Refresh(it.keyword))
|
||||||
|
}
|
||||||
|
val shouldLoad = LocalShouldLoad.current
|
||||||
|
LaunchedEffect(currentKeyword) {
|
||||||
|
if (currentKeyword.isNotEmpty() && keyword != currentKeyword) {
|
||||||
|
if (shouldLoad) {
|
||||||
|
viewModel.send(SearchForumUiIntent.Refresh(keyword))
|
||||||
|
} else {
|
||||||
|
viewModel.initialized = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateScreen(
|
||||||
|
isEmpty = isEmpty,
|
||||||
|
isError = error != null,
|
||||||
|
isLoading = isRefreshing,
|
||||||
|
onReload = { viewModel.send(SearchForumUiIntent.Refresh(keyword)) },
|
||||||
|
errorScreen = {
|
||||||
|
error?.let {
|
||||||
|
val (e) = it
|
||||||
|
ErrorScreen(error = e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
@ -151,6 +182,8 @@ fun SearchForumPage(
|
||||||
contentColor = ExtendedTheme.colors.primary,
|
contentColor = ExtendedTheme.colors.primary,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ data class SearchForumUiState(
|
||||||
val keyword: String = "",
|
val keyword: String = "",
|
||||||
val exactMatchForum: SearchForumBean.ForumInfoBean? = null,
|
val exactMatchForum: SearchForumBean.ForumInfoBean? = null,
|
||||||
val fuzzyMatchForumList: List<SearchForumBean.ForumInfoBean> = persistentListOf(),
|
val fuzzyMatchForumList: List<SearchForumBean.ForumInfoBean> = persistentListOf(),
|
||||||
val isRefreshing: Boolean = false,
|
val isRefreshing: Boolean = true,
|
||||||
val error: ImmutableHolder<Throwable>? = null,
|
val error: ImmutableHolder<Throwable>? = null,
|
||||||
) : UiState
|
) : UiState
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,9 @@ import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
|
@ -30,8 +32,10 @@ import com.huanchengfly.tieba.post.ui.common.theme.compose.pullRefreshIndicator
|
||||||
import com.huanchengfly.tieba.post.ui.page.LocalNavigator
|
import com.huanchengfly.tieba.post.ui.page.LocalNavigator
|
||||||
import com.huanchengfly.tieba.post.ui.page.destinations.ForumPageDestination
|
import com.huanchengfly.tieba.post.ui.page.destinations.ForumPageDestination
|
||||||
import com.huanchengfly.tieba.post.ui.page.destinations.ThreadPageDestination
|
import com.huanchengfly.tieba.post.ui.page.destinations.ThreadPageDestination
|
||||||
|
import com.huanchengfly.tieba.post.ui.page.search.SearchUiEvent
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.Avatar
|
import com.huanchengfly.tieba.post.ui.widgets.compose.Avatar
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.Card
|
import com.huanchengfly.tieba.post.ui.widgets.compose.Card
|
||||||
|
import com.huanchengfly.tieba.post.ui.widgets.compose.ErrorScreen
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.ForumInfoChip
|
import com.huanchengfly.tieba.post.ui.widgets.compose.ForumInfoChip
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoad
|
import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoad
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.LoadMoreLayout
|
import com.huanchengfly.tieba.post.ui.widgets.compose.LoadMoreLayout
|
||||||
|
|
@ -42,6 +46,7 @@ import com.huanchengfly.tieba.post.ui.widgets.compose.ThreadContent
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.ThreadReplyBtn
|
import com.huanchengfly.tieba.post.ui.widgets.compose.ThreadReplyBtn
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.ThreadShareBtn
|
import com.huanchengfly.tieba.post.ui.widgets.compose.ThreadShareBtn
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.UserHeader
|
import com.huanchengfly.tieba.post.ui.widgets.compose.UserHeader
|
||||||
|
import com.huanchengfly.tieba.post.ui.widgets.compose.states.StateScreen
|
||||||
import com.huanchengfly.tieba.post.utils.DateTimeUtils
|
import com.huanchengfly.tieba.post.utils.DateTimeUtils
|
||||||
import com.huanchengfly.tieba.post.utils.StringUtil
|
import com.huanchengfly.tieba.post.utils.StringUtil
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
@ -60,9 +65,13 @@ fun SearchThreadPage(
|
||||||
viewModel.send(SearchThreadUiIntent.Refresh(keyword, initialSortType))
|
viewModel.send(SearchThreadUiIntent.Refresh(keyword, initialSortType))
|
||||||
viewModel.initialized = true
|
viewModel.initialized = true
|
||||||
}
|
}
|
||||||
|
val currentKeyword by viewModel.uiState.collectPartialAsState(
|
||||||
|
prop1 = SearchThreadUiState::keyword,
|
||||||
|
initial = ""
|
||||||
|
)
|
||||||
val isRefreshing by viewModel.uiState.collectPartialAsState(
|
val isRefreshing by viewModel.uiState.collectPartialAsState(
|
||||||
prop1 = SearchThreadUiState::isRefreshing,
|
prop1 = SearchThreadUiState::isRefreshing,
|
||||||
initial = false
|
initial = true
|
||||||
)
|
)
|
||||||
val isLoadingMore by viewModel.uiState.collectPartialAsState(
|
val isLoadingMore by viewModel.uiState.collectPartialAsState(
|
||||||
prop1 = SearchThreadUiState::isLoadingMore,
|
prop1 = SearchThreadUiState::isLoadingMore,
|
||||||
|
|
@ -88,9 +97,13 @@ fun SearchThreadPage(
|
||||||
prop1 = SearchThreadUiState::sortType,
|
prop1 = SearchThreadUiState::sortType,
|
||||||
initial = initialSortType
|
initial = initialSortType
|
||||||
)
|
)
|
||||||
|
|
||||||
|
onGlobalEvent<SearchThreadUiEvent.SwitchSortType> {
|
||||||
|
viewModel.send(SearchThreadUiIntent.Refresh(keyword, it.sortType))
|
||||||
|
}
|
||||||
val shouldLoad = LocalShouldLoad.current
|
val shouldLoad = LocalShouldLoad.current
|
||||||
LaunchedEffect(keyword) {
|
LaunchedEffect(currentKeyword) {
|
||||||
if (viewModel.initialized) {
|
if (currentKeyword.isNotEmpty() && keyword != currentKeyword) {
|
||||||
if (shouldLoad) {
|
if (shouldLoad) {
|
||||||
viewModel.send(SearchThreadUiIntent.Refresh(keyword, sortType))
|
viewModel.send(SearchThreadUiIntent.Refresh(keyword, sortType))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -99,16 +112,32 @@ fun SearchThreadPage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onGlobalEvent<SearchThreadUiEvent.SwitchSortType> {
|
|
||||||
viewModel.send(SearchThreadUiIntent.Refresh(keyword, it.sortType))
|
|
||||||
}
|
|
||||||
|
|
||||||
val pullRefreshState = rememberPullRefreshState(
|
val pullRefreshState = rememberPullRefreshState(
|
||||||
refreshing = isRefreshing,
|
refreshing = isRefreshing,
|
||||||
onRefresh = { viewModel.send(SearchThreadUiIntent.Refresh(keyword, sortType)) }
|
onRefresh = { viewModel.send(SearchThreadUiIntent.Refresh(keyword, sortType)) }
|
||||||
)
|
)
|
||||||
val lazyListState = rememberLazyListState()
|
val lazyListState = rememberLazyListState()
|
||||||
|
|
||||||
|
val isEmpty by remember {
|
||||||
|
derivedStateOf { data.isEmpty() }
|
||||||
|
}
|
||||||
|
|
||||||
|
onGlobalEvent<SearchUiEvent.KeywordChanged> {
|
||||||
|
viewModel.send(SearchThreadUiIntent.Refresh(it.keyword, sortType))
|
||||||
|
}
|
||||||
|
|
||||||
|
StateScreen(
|
||||||
|
isEmpty = isEmpty,
|
||||||
|
isError = error != null,
|
||||||
|
isLoading = isRefreshing,
|
||||||
|
onReload = { viewModel.send(SearchThreadUiIntent.Refresh(keyword, sortType)) },
|
||||||
|
errorScreen = {
|
||||||
|
error?.let {
|
||||||
|
val (e) = it
|
||||||
|
ErrorScreen(error = e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
@ -155,6 +184,7 @@ fun SearchThreadPage(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ sealed interface SearchThreadPartialChange : PartialChange<SearchThreadUiState>
|
||||||
}
|
}
|
||||||
|
|
||||||
data class SearchThreadUiState(
|
data class SearchThreadUiState(
|
||||||
val isRefreshing: Boolean = false,
|
val isRefreshing: Boolean = true,
|
||||||
val isLoadingMore: Boolean = false,
|
val isLoadingMore: Boolean = false,
|
||||||
val error: ImmutableHolder<Throwable>? = null,
|
val error: ImmutableHolder<Throwable>? = null,
|
||||||
val currentPage: Int = 1,
|
val currentPage: Int = 1,
|
||||||
|
|
|
||||||
|
|
@ -31,15 +31,19 @@ import androidx.compose.ui.unit.dp
|
||||||
import com.huanchengfly.tieba.post.R
|
import com.huanchengfly.tieba.post.R
|
||||||
import com.huanchengfly.tieba.post.api.models.SearchUserBean
|
import com.huanchengfly.tieba.post.api.models.SearchUserBean
|
||||||
import com.huanchengfly.tieba.post.arch.collectPartialAsState
|
import com.huanchengfly.tieba.post.arch.collectPartialAsState
|
||||||
|
import com.huanchengfly.tieba.post.arch.onGlobalEvent
|
||||||
import com.huanchengfly.tieba.post.arch.pageViewModel
|
import com.huanchengfly.tieba.post.arch.pageViewModel
|
||||||
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.pullRefreshIndicator
|
import com.huanchengfly.tieba.post.ui.common.theme.compose.pullRefreshIndicator
|
||||||
import com.huanchengfly.tieba.post.ui.page.LocalNavigator
|
import com.huanchengfly.tieba.post.ui.page.LocalNavigator
|
||||||
|
import com.huanchengfly.tieba.post.ui.page.search.SearchUiEvent
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.Chip
|
import com.huanchengfly.tieba.post.ui.widgets.Chip
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.Avatar
|
import com.huanchengfly.tieba.post.ui.widgets.compose.Avatar
|
||||||
|
import com.huanchengfly.tieba.post.ui.widgets.compose.ErrorScreen
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoad
|
import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoad
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.LocalShouldLoad
|
import com.huanchengfly.tieba.post.ui.widgets.compose.LocalShouldLoad
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.Sizes
|
import com.huanchengfly.tieba.post.ui.widgets.compose.Sizes
|
||||||
|
import com.huanchengfly.tieba.post.ui.widgets.compose.states.StateScreen
|
||||||
import com.huanchengfly.tieba.post.utils.StringUtil
|
import com.huanchengfly.tieba.post.utils.StringUtil
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
|
|
@ -55,19 +59,17 @@ fun SearchUserPage(
|
||||||
viewModel.send(SearchUserUiIntent.Refresh(keyword))
|
viewModel.send(SearchUserUiIntent.Refresh(keyword))
|
||||||
viewModel.initialized = true
|
viewModel.initialized = true
|
||||||
}
|
}
|
||||||
val shouldLoad = LocalShouldLoad.current
|
val currentKeyword by viewModel.uiState.collectPartialAsState(
|
||||||
LaunchedEffect(keyword) {
|
prop1 = SearchUserUiState::keyword,
|
||||||
if (viewModel.initialized) {
|
initial = ""
|
||||||
if (shouldLoad) {
|
)
|
||||||
viewModel.send(SearchUserUiIntent.Refresh(keyword))
|
|
||||||
} else {
|
|
||||||
viewModel.initialized = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val isRefreshing by viewModel.uiState.collectPartialAsState(
|
val isRefreshing by viewModel.uiState.collectPartialAsState(
|
||||||
prop1 = SearchUserUiState::isRefreshing,
|
prop1 = SearchUserUiState::isRefreshing,
|
||||||
initial = false
|
initial = true
|
||||||
|
)
|
||||||
|
val error by viewModel.uiState.collectPartialAsState(
|
||||||
|
prop1 = SearchUserUiState::error,
|
||||||
|
initial = null
|
||||||
)
|
)
|
||||||
val pullRefreshState = rememberPullRefreshState(
|
val pullRefreshState = rememberPullRefreshState(
|
||||||
refreshing = isRefreshing,
|
refreshing = isRefreshing,
|
||||||
|
|
@ -89,6 +91,36 @@ fun SearchUserPage(
|
||||||
derivedStateOf { fuzzyMatch.isNotEmpty() }
|
derivedStateOf { fuzzyMatch.isNotEmpty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onGlobalEvent<SearchUiEvent.KeywordChanged> {
|
||||||
|
viewModel.send(SearchUserUiIntent.Refresh(it.keyword))
|
||||||
|
}
|
||||||
|
val shouldLoad = LocalShouldLoad.current
|
||||||
|
LaunchedEffect(currentKeyword) {
|
||||||
|
if (currentKeyword.isNotEmpty() && keyword != currentKeyword) {
|
||||||
|
if (shouldLoad) {
|
||||||
|
viewModel.send(SearchUserUiIntent.Refresh(keyword))
|
||||||
|
} else {
|
||||||
|
viewModel.initialized = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val isEmpty by remember {
|
||||||
|
derivedStateOf { !showExactMatchResult && !showFuzzyMatchResult }
|
||||||
|
}
|
||||||
|
|
||||||
|
StateScreen(
|
||||||
|
isEmpty = isEmpty,
|
||||||
|
isError = error != null,
|
||||||
|
isLoading = isRefreshing,
|
||||||
|
onReload = { viewModel.send(SearchUserUiIntent.Refresh(keyword)) },
|
||||||
|
errorScreen = {
|
||||||
|
error?.let {
|
||||||
|
val (e) = it
|
||||||
|
ErrorScreen(error = e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
@ -149,6 +181,7 @@ fun SearchUserPage(
|
||||||
contentColor = ExtendedTheme.colors.primary,
|
contentColor = ExtendedTheme.colors.primary,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ sealed interface SearchUserPartialChange : PartialChange<SearchUserUiState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
data class SearchUserUiState(
|
data class SearchUserUiState(
|
||||||
val isRefreshing: Boolean = false,
|
val isRefreshing: Boolean = true,
|
||||||
val error: ImmutableHolder<Throwable>? = null,
|
val error: ImmutableHolder<Throwable>? = null,
|
||||||
val keyword: String = "",
|
val keyword: String = "",
|
||||||
val exactMatch: SearchUserBean.UserBean? = null,
|
val exactMatch: SearchUserBean.UserBean? = null,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||||
|
|
@ -27,8 +29,11 @@ fun LazyLoad(
|
||||||
onLoad: () -> Unit,
|
onLoad: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val shouldLoad = LocalShouldLoad.current
|
val shouldLoad = LocalShouldLoad.current
|
||||||
LaunchedEffect(loaded, shouldLoad, onLoad) {
|
val curOnLoad by rememberUpdatedState(newValue = onLoad)
|
||||||
if (!loaded && shouldLoad) onLoad()
|
LaunchedEffect(loaded, shouldLoad) {
|
||||||
|
if (!loaded && shouldLoad) {
|
||||||
|
curOnLoad()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue