feat: 收藏页面状态

This commit is contained in:
HuanCheng65 2023-03-12 15:44:55 +08:00
parent 83a855b2d4
commit e3199c1d8d
No known key found for this signature in database
GPG Key ID: E9031EF91A805148
2 changed files with 88 additions and 55 deletions

View File

@ -20,7 +20,9 @@ import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material.rememberScaffoldState import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
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.graphics.Color import androidx.compose.ui.graphics.Color
@ -49,6 +51,7 @@ import com.huanchengfly.tieba.post.pxToSp
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.widgets.compose.Avatar import com.huanchengfly.tieba.post.ui.widgets.compose.Avatar
import com.huanchengfly.tieba.post.ui.widgets.compose.BackNavigationIcon import com.huanchengfly.tieba.post.ui.widgets.compose.BackNavigationIcon
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.LoadMoreLayout import com.huanchengfly.tieba.post.ui.widgets.compose.LoadMoreLayout
import com.huanchengfly.tieba.post.ui.widgets.compose.LongClickMenu import com.huanchengfly.tieba.post.ui.widgets.compose.LongClickMenu
@ -56,6 +59,7 @@ import com.huanchengfly.tieba.post.ui.widgets.compose.MyScaffold
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.TitleCentredToolbar import com.huanchengfly.tieba.post.ui.widgets.compose.TitleCentredToolbar
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.StringUtil import com.huanchengfly.tieba.post.utils.StringUtil
import com.huanchengfly.tieba.post.utils.StringUtil.getUsernameAnnotatedString import com.huanchengfly.tieba.post.utils.StringUtil.getUsernameAnnotatedString
import com.huanchengfly.tieba.post.utils.appPreferences import com.huanchengfly.tieba.post.utils.appPreferences
@ -100,12 +104,14 @@ fun ThreadStorePage(
prop1 = ThreadStoreUiState::data, prop1 = ThreadStoreUiState::data,
initial = emptyList() initial = emptyList()
) )
val error by viewModel.uiState.collectPartialAsState(
prop1 = ThreadStoreUiState::error,
initial = null
)
val isError by remember { derivedStateOf { error != null } }
val context = LocalContext.current val context = LocalContext.current
val scaffoldState = rememberScaffoldState() val scaffoldState = rememberScaffoldState()
val pullRefreshState = rememberPullRefreshState(
refreshing = isRefreshing,
onRefresh = { viewModel.send(ThreadStoreUiIntent.Refresh) })
viewModel.onEvent<ThreadStoreUiEvent.Delete.Failure> { viewModel.onEvent<ThreadStoreUiEvent.Delete.Failure> {
scaffoldState.snackbarHostState.showSnackbar( scaffoldState.snackbarHostState.showSnackbar(
context.getString( context.getString(
@ -128,64 +134,87 @@ fun ThreadStorePage(
} }
) )
} }
) { paddingValues -> ) { contentPaddings ->
val textMeasurer = rememberTextMeasurer() val textMeasurer = rememberTextMeasurer()
Box(modifier = Modifier.pullRefresh(pullRefreshState)) { StateScreen(
LoadMoreLayout( isEmpty = data.isEmpty(),
isLoading = isLoadingMore, isError = isError,
onLoadMore = { viewModel.send(ThreadStoreUiIntent.LoadMore(currentPage + 1)) }, isLoading = isRefreshing,
loadEnd = !hasMore modifier = Modifier.padding(contentPaddings),
) { onReload = {
LazyColumn(contentPadding = paddingValues) { viewModel.send(ThreadStoreUiIntent.Refresh)
items( },
items = data, errorScreen = {
key = { it.threadId } error?.let {
) { info -> val (e) = it
LongClickMenu( ErrorScreen(error = e)
menuContent = { }
DropdownMenuItem(onClick = { }
viewModel.send( ) {
ThreadStoreUiIntent.Delete( val pullRefreshState = rememberPullRefreshState(
info.threadId refreshing = isRefreshing,
) onRefresh = { viewModel.send(ThreadStoreUiIntent.Refresh) }
) )
}) {
Text(text = stringResource(id = R.string.title_collect_on)) Box(modifier = Modifier.pullRefresh(pullRefreshState)) {
} LoadMoreLayout(
}, isLoading = isLoadingMore,
onClick = { onLoadMore = { viewModel.send(ThreadStoreUiIntent.LoadMore(currentPage + 1)) },
ThreadActivity.launch( loadEnd = !hasMore
context, ) {
info.threadId, LazyColumn {
info.markPid, items(
context.appPreferences.collectThreadSeeLz, items = data,
"collect", key = { it.threadId }
info.maxPid ) { info ->
) LongClickMenu(
} menuContent = {
) { DropdownMenuItem(onClick = {
StoreItem( viewModel.send(
info = info, ThreadStoreUiIntent.Delete(
onUserClick = { info.threadId
info.author.lzUid?.let { )
UserActivity.launch(
context,
it, StringUtil.getAvatarUrl(info.author.userPortrait)
) )
}) {
Text(text = stringResource(id = R.string.title_collect_on))
} }
}, },
textMeasurer = textMeasurer onClick = {
) ThreadActivity.launch(
context,
info.threadId,
info.markPid,
context.appPreferences.collectThreadSeeLz,
"collect",
info.maxPid
)
}
) {
StoreItem(
info = info,
onUserClick = {
info.author.lzUid?.let {
UserActivity.launch(
context,
it,
StringUtil.getAvatarUrl(info.author.userPortrait)
)
}
},
textMeasurer = textMeasurer
)
}
} }
} }
} }
PullRefreshIndicator(
refreshing = isRefreshing,
state = pullRefreshState,
modifier = Modifier.align(Alignment.TopCenter)
)
} }
PullRefreshIndicator(
refreshing = isRefreshing,
state = pullRefreshState,
modifier = Modifier.align(Alignment.TopCenter)
)
} }
} }
} }

View File

@ -6,11 +6,13 @@ import com.huanchengfly.tieba.post.api.models.ThreadStoreBean
import com.huanchengfly.tieba.post.api.retrofit.exception.getErrorCode import com.huanchengfly.tieba.post.api.retrofit.exception.getErrorCode
import com.huanchengfly.tieba.post.api.retrofit.exception.getErrorMessage import com.huanchengfly.tieba.post.api.retrofit.exception.getErrorMessage
import com.huanchengfly.tieba.post.arch.BaseViewModel import com.huanchengfly.tieba.post.arch.BaseViewModel
import com.huanchengfly.tieba.post.arch.ImmutableHolder
import com.huanchengfly.tieba.post.arch.PartialChange import com.huanchengfly.tieba.post.arch.PartialChange
import com.huanchengfly.tieba.post.arch.PartialChangeProducer import com.huanchengfly.tieba.post.arch.PartialChangeProducer
import com.huanchengfly.tieba.post.arch.UiEvent import com.huanchengfly.tieba.post.arch.UiEvent
import com.huanchengfly.tieba.post.arch.UiIntent import com.huanchengfly.tieba.post.arch.UiIntent
import com.huanchengfly.tieba.post.arch.UiState import com.huanchengfly.tieba.post.arch.UiState
import com.huanchengfly.tieba.post.arch.wrapImmutable
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -103,13 +105,14 @@ sealed interface ThreadStoreUiIntent : UiIntent {
sealed interface ThreadStorePartialChange : PartialChange<ThreadStoreUiState> { sealed interface ThreadStorePartialChange : PartialChange<ThreadStoreUiState> {
sealed class Refresh : ThreadStorePartialChange { sealed class Refresh : ThreadStorePartialChange {
override fun reduce(oldState: ThreadStoreUiState): ThreadStoreUiState = when (this) { override fun reduce(oldState: ThreadStoreUiState): ThreadStoreUiState = when (this) {
is Failure -> oldState.copy(isRefreshing = false) is Failure -> oldState.copy(isRefreshing = false, error = wrapImmutable(error))
Start -> oldState.copy(isRefreshing = true) Start -> oldState.copy(isRefreshing = true)
is Success -> oldState.copy( is Success -> oldState.copy(
isRefreshing = false, isRefreshing = false,
data = data, data = data,
currentPage = 0, currentPage = 0,
hasMore = hasMore hasMore = hasMore,
error = null
) )
} }
@ -171,7 +174,8 @@ data class ThreadStoreUiState(
val isLoadingMore: Boolean = false, val isLoadingMore: Boolean = false,
val hasMore: Boolean = true, val hasMore: Boolean = true,
val currentPage: Int = 1, val currentPage: Int = 1,
val data: List<ThreadStoreBean.ThreadStoreInfo> = emptyList() val data: List<ThreadStoreBean.ThreadStoreInfo> = emptyList(),
val error: ImmutableHolder<Throwable>? = null
) : UiState ) : UiState
sealed interface ThreadStoreUiEvent : UiEvent { sealed interface ThreadStoreUiEvent : UiEvent {