feat: 动态页面状态屏

This commit is contained in:
HuanCheng65 2023-10-05 02:06:52 +08:00
parent 71a11f3496
commit a24d22e3f3
No known key found for this signature in database
GPG Key ID: 5EC9DD60A32C7360
2 changed files with 112 additions and 72 deletions

View File

@ -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)
}
}
}
}

View File

@ -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<PersonalizedUiState>
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<ThreadItemData>,
@ -305,10 +309,14 @@ sealed interface PersonalizedPartialChange : PartialChange<PersonalizedUiState>
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<PersonalizedUiState>
data class PersonalizedUiState(
val isRefreshing: Boolean = true,
val isLoadingMore: Boolean = false,
val error: ImmutableHolder<Throwable>? = null,
val currentPage: Int = 1,
val data: ImmutableList<ThreadItemData> = persistentListOf(),
val hiddenThreadIds: ImmutableList<Long> = persistentListOf(),