feat: 查看吧信息
This commit is contained in:
parent
58459c8f8d
commit
8ec45b90db
|
|
@ -9,7 +9,7 @@ import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.animation.expandVertically
|
import androidx.compose.animation.expandVertically
|
||||||
import androidx.compose.animation.shrinkVertically
|
import androidx.compose.animation.shrinkVertically
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.gestures.Orientation
|
import androidx.compose.foundation.gestures.Orientation
|
||||||
import androidx.compose.foundation.gestures.rememberScrollableState
|
import androidx.compose.foundation.gestures.rememberScrollableState
|
||||||
import androidx.compose.foundation.gestures.scrollable
|
import androidx.compose.foundation.gestures.scrollable
|
||||||
|
|
@ -104,6 +104,7 @@ import com.huanchengfly.tieba.post.toastShort
|
||||||
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.page.LocalNavigator
|
import com.huanchengfly.tieba.post.ui.page.LocalNavigator
|
||||||
import com.huanchengfly.tieba.post.ui.page.ProvideNavigator
|
import com.huanchengfly.tieba.post.ui.page.ProvideNavigator
|
||||||
|
import com.huanchengfly.tieba.post.ui.page.destinations.ForumDetailPageDestination
|
||||||
import com.huanchengfly.tieba.post.ui.page.forum.threadlist.ForumThreadListPage
|
import com.huanchengfly.tieba.post.ui.page.forum.threadlist.ForumThreadListPage
|
||||||
import com.huanchengfly.tieba.post.ui.page.forum.threadlist.ForumThreadListUiEvent
|
import com.huanchengfly.tieba.post.ui.page.forum.threadlist.ForumThreadListUiEvent
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.Avatar
|
import com.huanchengfly.tieba.post.ui.widgets.compose.Avatar
|
||||||
|
|
@ -113,7 +114,6 @@ 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.ConfirmDialog
|
import com.huanchengfly.tieba.post.ui.widgets.compose.ConfirmDialog
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.FeedCardPlaceholder
|
import com.huanchengfly.tieba.post.ui.widgets.compose.FeedCardPlaceholder
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.HorizontalDivider
|
|
||||||
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.MenuScope
|
import com.huanchengfly.tieba.post.ui.widgets.compose.MenuScope
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.MyScaffold
|
import com.huanchengfly.tieba.post.ui.widgets.compose.MyScaffold
|
||||||
|
|
@ -197,29 +197,15 @@ private fun ForumHeaderPlaceholder(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.clip(RoundedCornerShape(8.dp))
|
|
||||||
.placeholder(
|
|
||||||
visible = true,
|
|
||||||
highlight = PlaceholderHighlight.fade(),
|
|
||||||
)
|
|
||||||
.padding(vertical = 20.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
) {
|
|
||||||
StatCardItem(
|
|
||||||
statNum = 0,
|
|
||||||
statText = stringResource(id = R.string.text_stat_follow)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ForumHeader(
|
private fun ForumHeader(
|
||||||
forumInfoImmutableHolder: ImmutableHolder<ForumInfo>,
|
forumInfoImmutableHolder: ImmutableHolder<ForumInfo>,
|
||||||
|
onOpenForumInfo: () -> Unit,
|
||||||
onBtnClick: () -> Unit,
|
onBtnClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val (forum) = forumInfoImmutableHolder
|
val (forum) = forumInfoImmutableHolder
|
||||||
Column(
|
Column(
|
||||||
|
|
@ -239,12 +225,27 @@ private fun ForumHeader(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
Row(
|
||||||
text = stringResource(id = R.string.title_forum, forum.name),
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
style = MaterialTheme.typography.h6,
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
maxLines = 1,
|
modifier = Modifier.clickable(
|
||||||
overflow = TextOverflow.Ellipsis,
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
)
|
indication = null,
|
||||||
|
onClick = onOpenForumInfo
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.title_forum, forum.name),
|
||||||
|
style = MaterialTheme.typography.h6,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
// Icon(
|
||||||
|
// imageVector = Icons.Rounded.KeyboardArrowRight,
|
||||||
|
// contentDescription = null,
|
||||||
|
// modifier = Modifier.size(16.dp)
|
||||||
|
// )
|
||||||
|
}
|
||||||
AnimatedVisibility(visible = forum.is_like == 1) {
|
AnimatedVisibility(visible = forum.is_like == 1) {
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
|
|
@ -301,28 +302,28 @@ private fun ForumHeader(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Row(
|
// Row(
|
||||||
modifier = Modifier
|
// modifier = Modifier
|
||||||
.clip(RoundedCornerShape(8.dp))
|
// .clip(RoundedCornerShape(8.dp))
|
||||||
.background(color = ExtendedTheme.colors.chip)
|
// .background(color = ExtendedTheme.colors.chip)
|
||||||
.padding(vertical = 20.dp),
|
// .padding(vertical = 20.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
// verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
// ) {
|
||||||
StatCardItem(
|
// StatCardItem(
|
||||||
statNum = forum.member_num,
|
// statNum = forum.member_num,
|
||||||
statText = stringResource(id = R.string.text_stat_follow)
|
// statText = stringResource(id = R.string.text_stat_follow)
|
||||||
)
|
// )
|
||||||
HorizontalDivider(color = Color(if (ExtendedTheme.colors.isNightMode) 0xFF808080 else 0xFFDEDEDE))
|
// HorizontalDivider(color = Color(if (ExtendedTheme.colors.isNightMode) 0xFF808080 else 0xFFDEDEDE))
|
||||||
StatCardItem(
|
// StatCardItem(
|
||||||
statNum = forum.thread_num,
|
// statNum = forum.thread_num,
|
||||||
statText = stringResource(id = R.string.text_stat_threads)
|
// statText = stringResource(id = R.string.text_stat_threads)
|
||||||
)
|
// )
|
||||||
HorizontalDivider(color = Color(if (ExtendedTheme.colors.isNightMode) 0xFF808080 else 0xFFDEDEDE))
|
// HorizontalDivider(color = Color(if (ExtendedTheme.colors.isNightMode) 0xFF808080 else 0xFFDEDEDE))
|
||||||
StatCardItem(
|
// StatCardItem(
|
||||||
statNum = forum.post_num,
|
// statNum = forum.post_num,
|
||||||
statText = stringResource(id = R.string.title_stat_posts_num)
|
// statText = stringResource(id = R.string.title_stat_posts_num)
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -651,11 +652,14 @@ fun ForumPage(
|
||||||
),
|
),
|
||||||
exit = shrinkVertically()
|
exit = shrinkVertically()
|
||||||
) {
|
) {
|
||||||
if (forumInfo != null) {
|
forumInfo?.let {
|
||||||
ForumHeader(
|
ForumHeader(
|
||||||
forumInfoImmutableHolder = forumInfo!!,
|
forumInfoImmutableHolder = it,
|
||||||
|
onOpenForumInfo = {
|
||||||
|
navigator.navigate(ForumDetailPageDestination(forumId = it.get { this.id }))
|
||||||
|
},
|
||||||
onBtnClick = {
|
onBtnClick = {
|
||||||
val (forum) = forumInfo!!
|
val (forum) = it
|
||||||
when {
|
when {
|
||||||
forum.is_like != 1 -> viewModel.send(
|
forum.is_like != 1 -> viewModel.send(
|
||||||
ForumUiIntent.Like(
|
ForumUiIntent.Like(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,232 @@
|
||||||
|
package com.huanchengfly.tieba.post.ui.page.forum.detail
|
||||||
|
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.huanchengfly.tieba.post.R
|
||||||
|
import com.huanchengfly.tieba.post.api.models.protos.PbContent
|
||||||
|
import com.huanchengfly.tieba.post.api.models.protos.RecommendForumInfo
|
||||||
|
import com.huanchengfly.tieba.post.api.models.protos.plainText
|
||||||
|
import com.huanchengfly.tieba.post.arch.ImmutableHolder
|
||||||
|
import com.huanchengfly.tieba.post.arch.collectPartialAsState
|
||||||
|
import com.huanchengfly.tieba.post.arch.getOrNull
|
||||||
|
import com.huanchengfly.tieba.post.arch.pageViewModel
|
||||||
|
import com.huanchengfly.tieba.post.arch.wrapImmutable
|
||||||
|
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.widgets.Chip
|
||||||
|
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.Container
|
||||||
|
import com.huanchengfly.tieba.post.ui.widgets.compose.ErrorScreen
|
||||||
|
import com.huanchengfly.tieba.post.ui.widgets.compose.HorizontalDivider
|
||||||
|
import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoad
|
||||||
|
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.TitleCentredToolbar
|
||||||
|
import com.huanchengfly.tieba.post.ui.widgets.compose.states.StateScreen
|
||||||
|
import com.huanchengfly.tieba.post.utils.StringUtil.getShortNumString
|
||||||
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
|
|
||||||
|
@Destination
|
||||||
|
@Composable
|
||||||
|
fun ForumDetailPage(
|
||||||
|
forumId: Long,
|
||||||
|
navigator: DestinationsNavigator,
|
||||||
|
viewModel: ForumDetailViewModel = pageViewModel(),
|
||||||
|
) {
|
||||||
|
LazyLoad(loaded = viewModel.initialized) {
|
||||||
|
viewModel.send(ForumDetailUiIntent.Load(forumId))
|
||||||
|
viewModel.initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val isLoading by viewModel.uiState.collectPartialAsState(
|
||||||
|
prop1 = ForumDetailUiState::isLoading,
|
||||||
|
initial = true
|
||||||
|
)
|
||||||
|
val error by viewModel.uiState.collectPartialAsState(
|
||||||
|
prop1 = ForumDetailUiState::error,
|
||||||
|
initial = null
|
||||||
|
)
|
||||||
|
val forumInfo by viewModel.uiState.collectPartialAsState(
|
||||||
|
prop1 = ForumDetailUiState::forumInfo,
|
||||||
|
initial = null
|
||||||
|
)
|
||||||
|
|
||||||
|
val isEmpty by remember {
|
||||||
|
derivedStateOf { forumInfo == null }
|
||||||
|
}
|
||||||
|
val isError by remember {
|
||||||
|
derivedStateOf { error != null }
|
||||||
|
}
|
||||||
|
|
||||||
|
StateScreen(
|
||||||
|
isEmpty = isEmpty,
|
||||||
|
isError = isError,
|
||||||
|
isLoading = isLoading,
|
||||||
|
onReload = {
|
||||||
|
viewModel.send(ForumDetailUiIntent.Load(forumId))
|
||||||
|
},
|
||||||
|
errorScreen = {
|
||||||
|
ErrorScreen(error = error.getOrNull())
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
MyScaffold(
|
||||||
|
topBar = {
|
||||||
|
TitleCentredToolbar(
|
||||||
|
title = {
|
||||||
|
Text(text = stringResource(id = R.string.title_forum_info))
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
BackNavigationIcon {
|
||||||
|
navigator.navigateUp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { paddingValues ->
|
||||||
|
Container(modifier = Modifier.padding(paddingValues)) {
|
||||||
|
forumInfo?.let {
|
||||||
|
ForumDetailContent(
|
||||||
|
forumInfo = it,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ForumDetailContent(
|
||||||
|
forumInfo: ImmutableHolder<RecommendForumInfo>,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
val intro = remember(forumInfo) {
|
||||||
|
forumInfo.get { content.plainText }
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
.verticalScroll(rememberScrollState()),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Avatar(
|
||||||
|
data = forumInfo.get { avatar },
|
||||||
|
size = Sizes.Medium,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.title_forum, forumInfo.get { forum_name }),
|
||||||
|
style = MaterialTheme.typography.h6
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(8.dp))
|
||||||
|
.background(color = ExtendedTheme.colors.chip)
|
||||||
|
.padding(vertical = 20.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
StatCardItem(
|
||||||
|
statNum = forumInfo.get { member_count },
|
||||||
|
statText = stringResource(id = R.string.text_stat_follow)
|
||||||
|
)
|
||||||
|
HorizontalDivider(color = Color(if (ExtendedTheme.colors.isNightMode) 0xFF808080 else 0xFFDEDEDE))
|
||||||
|
StatCardItem(
|
||||||
|
statNum = forumInfo.get { thread_count },
|
||||||
|
statText = stringResource(id = R.string.text_stat_threads)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Chip(text = stringResource(id = R.string.title_forum_intro))
|
||||||
|
Column {
|
||||||
|
Text(text = forumInfo.get { slogan }, style = MaterialTheme.typography.body1)
|
||||||
|
Text(text = intro, style = MaterialTheme.typography.body1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun RowScope.StatCardItem(
|
||||||
|
statNum: Int,
|
||||||
|
statText: String,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = statNum.getShortNumString(),
|
||||||
|
fontSize = 20.sp,
|
||||||
|
fontFamily = FontFamily(
|
||||||
|
Typeface.createFromAsset(
|
||||||
|
LocalContext.current.assets,
|
||||||
|
"bebas.ttf"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(2.dp))
|
||||||
|
Text(
|
||||||
|
text = statText,
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = ExtendedTheme.colors.textSecondary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview("ForumDetailPage", backgroundColor = 0xFFFFFFFF, showBackground = true)
|
||||||
|
@Composable
|
||||||
|
fun PreviewForumDetailPage() {
|
||||||
|
TiebaLiteTheme {
|
||||||
|
ForumDetailContent(
|
||||||
|
forumInfo = RecommendForumInfo(
|
||||||
|
forum_name = "minecraft",
|
||||||
|
slogan = "位于百度贴吧的像素点之家",
|
||||||
|
content = listOf(
|
||||||
|
PbContent(
|
||||||
|
type = 0,
|
||||||
|
text = "minecraft……",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
member_count = 2520287,
|
||||||
|
thread_count = 31531580
|
||||||
|
).wrapImmutable(),
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
package com.huanchengfly.tieba.post.ui.page.forum.detail
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.huanchengfly.tieba.post.api.TiebaApi
|
||||||
|
import com.huanchengfly.tieba.post.api.models.protos.RecommendForumInfo
|
||||||
|
import com.huanchengfly.tieba.post.api.models.protos.getForumDetail.GetForumDetailResponse
|
||||||
|
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.PartialChangeProducer
|
||||||
|
import com.huanchengfly.tieba.post.arch.UiEvent
|
||||||
|
import com.huanchengfly.tieba.post.arch.UiIntent
|
||||||
|
import com.huanchengfly.tieba.post.arch.UiState
|
||||||
|
import com.huanchengfly.tieba.post.arch.wrapImmutable
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.filterIsInstance
|
||||||
|
import kotlinx.coroutines.flow.flatMapConcat
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.merge
|
||||||
|
import kotlinx.coroutines.flow.onStart
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class ForumDetailViewModel @Inject constructor() :
|
||||||
|
BaseViewModel<ForumDetailUiIntent, ForumDetailPartialChange, ForumDetailUiState, UiEvent>() {
|
||||||
|
override fun createInitialState(): ForumDetailUiState = ForumDetailUiState()
|
||||||
|
|
||||||
|
override fun createPartialChangeProducer(): PartialChangeProducer<ForumDetailUiIntent, ForumDetailPartialChange, ForumDetailUiState> =
|
||||||
|
ForumDetailPartialChangeProducer
|
||||||
|
|
||||||
|
private object ForumDetailPartialChangeProducer :
|
||||||
|
PartialChangeProducer<ForumDetailUiIntent, ForumDetailPartialChange, ForumDetailUiState> {
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
override fun toPartialChangeFlow(intentFlow: Flow<ForumDetailUiIntent>): Flow<ForumDetailPartialChange> =
|
||||||
|
merge(
|
||||||
|
intentFlow.filterIsInstance<ForumDetailUiIntent.Load>()
|
||||||
|
.flatMapConcat { it.producePartialChange() }
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun ForumDetailUiIntent.Load.producePartialChange(): Flow<ForumDetailPartialChange.Load> =
|
||||||
|
TiebaApi.getInstance()
|
||||||
|
.getForumDetailFlow(forumId)
|
||||||
|
.map<GetForumDetailResponse, ForumDetailPartialChange.Load> {
|
||||||
|
val forumInfo = it.data_?.forum_info
|
||||||
|
checkNotNull(forumInfo) { "forumInfo is null" }
|
||||||
|
ForumDetailPartialChange.Load.Success(
|
||||||
|
forumInfo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.onStart { emit(ForumDetailPartialChange.Load.Start) }
|
||||||
|
.catch { emit(ForumDetailPartialChange.Load.Failure(it)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface ForumDetailUiIntent : UiIntent {
|
||||||
|
data class Load(val forumId: Long) : ForumDetailUiIntent
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface ForumDetailPartialChange : PartialChange<ForumDetailUiState> {
|
||||||
|
sealed class Load : ForumDetailPartialChange {
|
||||||
|
override fun reduce(oldState: ForumDetailUiState): ForumDetailUiState = when (this) {
|
||||||
|
Start -> oldState.copy(
|
||||||
|
isLoading = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
is Success -> oldState.copy(
|
||||||
|
isLoading = false,
|
||||||
|
error = null,
|
||||||
|
forumInfo = forumInfo.wrapImmutable()
|
||||||
|
)
|
||||||
|
|
||||||
|
is Failure -> oldState.copy(
|
||||||
|
isLoading = false,
|
||||||
|
error = error.wrapImmutable()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
data object Start : Load()
|
||||||
|
|
||||||
|
data class Success(val forumInfo: RecommendForumInfo) : Load()
|
||||||
|
|
||||||
|
data class Failure(val error: Throwable) : Load()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class ForumDetailUiState(
|
||||||
|
val isLoading: Boolean = true,
|
||||||
|
val error: ImmutableHolder<Throwable>? = null,
|
||||||
|
|
||||||
|
val forumInfo: ImmutableHolder<RecommendForumInfo>? = null,
|
||||||
|
) : UiState
|
||||||
|
|
@ -742,6 +742,7 @@
|
||||||
<string name="button_open">打开</string>
|
<string name="button_open">打开</string>
|
||||||
<string name="btn_hide">隐藏</string>
|
<string name="btn_hide">隐藏</string>
|
||||||
<string name="desc_search_sug">搜索联想:%s</string>
|
<string name="desc_search_sug">搜索联想:%s</string>
|
||||||
|
<string name="title_forum_intro">本吧简介</string>
|
||||||
<string name="title_forum_rule">本吧吧规</string>
|
<string name="title_forum_rule">本吧吧规</string>
|
||||||
<string name="desc_forum_rule">吧规</string>
|
<string name="desc_forum_rule">吧规</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue