diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/explore/personalized/PersonalizedPage.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/explore/personalized/PersonalizedPage.kt index 438fd573..4072f247 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/explore/personalized/PersonalizedPage.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/explore/personalized/PersonalizedPage.kt @@ -33,6 +33,7 @@ import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf @@ -62,11 +63,13 @@ import com.huanchengfly.tieba.post.ui.page.destinations.ThreadPageDestination import com.huanchengfly.tieba.post.ui.widgets.compose.BlockTip import com.huanchengfly.tieba.post.ui.widgets.compose.BlockableContent import com.huanchengfly.tieba.post.ui.widgets.compose.Container +import com.huanchengfly.tieba.post.ui.widgets.compose.ErrorScreen import com.huanchengfly.tieba.post.ui.widgets.compose.FeedCard 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.MyLazyColumn import com.huanchengfly.tieba.post.ui.widgets.compose.VerticalDivider +import com.huanchengfly.tieba.post.ui.widgets.compose.states.StateScreen import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.delay @@ -98,6 +101,10 @@ fun PersonalizedPage( prop1 = PersonalizedUiState::data, initial = persistentListOf() ) + val error by viewModel.uiState.collectPartialAsState( + prop1 = PersonalizedUiState::error, + initial = null + ) val refreshPosition by viewModel.uiState.collectPartialAsState( prop1 = PersonalizedUiState::refreshPosition, initial = 0 @@ -112,6 +119,16 @@ fun PersonalizedPage( ) val lazyListState = rememberLazyListState() viewModel.bindScrollToTopEvent(lazyListState = lazyListState) + val isEmpty by remember { + derivedStateOf { + data.isEmpty() + } + } + val isError by remember { + derivedStateOf { + error != null + } + } var refreshCount by remember { mutableIntStateOf(0) } @@ -147,77 +164,91 @@ fun PersonalizedPage( // } // } // } - Box(modifier = Modifier.pullRefresh(pullRefreshState)) { - LoadMoreLayout( - isLoading = isLoadingMore, - onLoadMore = { viewModel.send(PersonalizedUiIntent.LoadMore(currentPage + 1)) }, - loadEnd = false, - lazyListState = lazyListState, - isEmpty = data.isEmpty() - ) { - FeedList( - state = lazyListState, - dataProvider = { data }, - refreshPositionProvider = { refreshPosition }, - hiddenThreadIdsProvider = { hiddenThreadIds }, - onItemClick = { - navigator.navigate( - ThreadPageDestination( - it.id, - it.forumId, - threadInfo = it - ) - ) - }, - onItemReplyClick = { - navigator.navigate( - ThreadPageDestination( - it.id, - it.forumId, - scrollToReply = true - ) - ) - }, - onAgree = { - viewModel.send( - PersonalizedUiIntent.Agree( - it.threadId, - it.firstPostId, - it.agree?.hasAgree ?: 0 - ) - ) - }, - onDislike = { item, clickTime, reasons -> - viewModel.send( - PersonalizedUiIntent.Dislike( - item.forumInfo?.id ?: 0, - item.threadId, - reasons, - clickTime - ) - ) - }, - onRefresh = { viewModel.send(PersonalizedUiIntent.Refresh) } - ) { - navigator.navigate(ForumPageDestination(it)) + StateScreen( + isEmpty = isEmpty, + isError = isError, + isLoading = isRefreshing, + onReload = { viewModel.send(PersonalizedUiIntent.Refresh) }, + errorScreen = { + error?.let { + ErrorScreen( + error = it.get() + ) } } + ) { + Box(modifier = Modifier.pullRefresh(pullRefreshState)) { + LoadMoreLayout( + isLoading = isLoadingMore, + onLoadMore = { viewModel.send(PersonalizedUiIntent.LoadMore(currentPage + 1)) }, + loadEnd = false, + lazyListState = lazyListState, + isEmpty = data.isEmpty() + ) { + FeedList( + state = lazyListState, + dataProvider = { data }, + refreshPositionProvider = { refreshPosition }, + hiddenThreadIdsProvider = { hiddenThreadIds }, + onItemClick = { + navigator.navigate( + ThreadPageDestination( + it.id, + it.forumId, + threadInfo = it + ) + ) + }, + onItemReplyClick = { + navigator.navigate( + ThreadPageDestination( + it.id, + it.forumId, + scrollToReply = true + ) + ) + }, + onAgree = { + viewModel.send( + PersonalizedUiIntent.Agree( + it.threadId, + it.firstPostId, + it.agree?.hasAgree ?: 0 + ) + ) + }, + onDislike = { item, clickTime, reasons -> + viewModel.send( + PersonalizedUiIntent.Dislike( + item.forumInfo?.id ?: 0, + item.threadId, + reasons, + clickTime + ) + ) + }, + onRefresh = { viewModel.send(PersonalizedUiIntent.Refresh) } + ) { + navigator.navigate(ForumPageDestination(it)) + } + } - PullRefreshIndicator( - refreshing = isRefreshing, - state = pullRefreshState, - modifier = Modifier.align(Alignment.TopCenter), - backgroundColor = ExtendedTheme.colors.pullRefreshIndicator, - contentColor = ExtendedTheme.colors.primary, - ) + PullRefreshIndicator( + refreshing = isRefreshing, + state = pullRefreshState, + modifier = Modifier.align(Alignment.TopCenter), + backgroundColor = ExtendedTheme.colors.pullRefreshIndicator, + contentColor = ExtendedTheme.colors.primary, + ) - AnimatedVisibility( - visible = showRefreshTip, - enter = fadeIn() + slideInVertically(), - exit = slideOutVertically() + fadeOut(), - modifier = Modifier.align(Alignment.TopCenter) - ) { - RefreshTip(refreshCount = refreshCount) + AnimatedVisibility( + visible = showRefreshTip, + enter = fadeIn() + slideInVertically(), + exit = slideOutVertically() + fadeOut(), + modifier = Modifier.align(Alignment.TopCenter) + ) { + RefreshTip(refreshCount = refreshCount) + } } } } diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/explore/personalized/PersonalizedViewModel.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/explore/personalized/PersonalizedViewModel.kt index 17e707fa..defe67d2 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/explore/personalized/PersonalizedViewModel.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/explore/personalized/PersonalizedViewModel.kt @@ -145,7 +145,7 @@ class PersonalizedViewModel @Inject constructor() : } sealed interface PersonalizedUiIntent : UiIntent { - object Refresh : PersonalizedUiIntent + data object Refresh : PersonalizedUiIntent data class LoadMore(val page: Int) : PersonalizedUiIntent @@ -282,10 +282,14 @@ sealed interface PersonalizedPartialChange : PartialChange data = (data + oldState.data).toImmutableList(), refreshPosition = if (oldState.data.isEmpty()) 0 else data.size ) - is Failure -> oldState.copy(isRefreshing = false) + + is Failure -> oldState.copy( + isRefreshing = false, + error = error.wrapImmutable() + ) } - object Start: Refresh() + data object Start : Refresh() data class Success( val data: List, @@ -305,10 +309,14 @@ sealed interface PersonalizedPartialChange : PartialChange currentPage = currentPage, data = (oldState.data + data).toImmutableList(), ) - is Failure -> oldState.copy(isLoadingMore = false) + + is Failure -> oldState.copy( + isLoadingMore = false, + error = error.wrapImmutable() + ) } - object Start: LoadMore() + data object Start : LoadMore() data class Success( val currentPage: Int, @@ -325,6 +333,7 @@ sealed interface PersonalizedPartialChange : PartialChange data class PersonalizedUiState( val isRefreshing: Boolean = true, val isLoadingMore: Boolean = false, + val error: ImmutableHolder? = null, val currentPage: Int = 1, val data: ImmutableList = persistentListOf(), val hiddenThreadIds: ImmutableList = persistentListOf(),