pref: 楼中楼内容计算在 ViewModel 进行

This commit is contained in:
HuanCheng65 2023-07-21 15:15:52 +08:00
parent 333b50a090
commit 7139bb134b
No known key found for this signature in database
GPG Key ID: 5EC9DD60A32C7360
4 changed files with 106 additions and 86 deletions

View File

@ -2,9 +2,11 @@ package com.huanchengfly.tieba.post.api.models.protos
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withAnnotation
import androidx.compose.ui.text.withStyle
import com.huanchengfly.tieba.post.App
@ -20,7 +22,9 @@ import com.huanchengfly.tieba.post.ui.utils.getPhotoViewData
import com.huanchengfly.tieba.post.utils.EmoticonManager
import com.huanchengfly.tieba.post.utils.EmoticonUtil.emoticonString
import com.huanchengfly.tieba.post.utils.ImageUtil
import com.huanchengfly.tieba.post.utils.StringUtil
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
val ThreadInfo.abstractText: String
@ -325,3 +329,38 @@ val User.bawuType: String?
get() = if (is_bawu == 1) {
if (bawu_type == "manager") "吧主" else "小吧主"
} else null
val Post.subPostContents: ImmutableList<AnnotatedString>
get() = sub_post_list?.sub_post_list?.map { it.contentText }?.toImmutableList()
?: persistentListOf()
@OptIn(ExperimentalTextApi::class)
val SubPostList.contentText: AnnotatedString
get() {
val context = App.INSTANCE
val accentColor = Color(ThemeUtils.getColorByAttr(context, R.attr.colorNewAccent))
val userNameString = buildAnnotatedString {
withStyle(
style = SpanStyle(
color = accentColor,
fontWeight = FontWeight.Bold
)
) {
withAnnotation("user", "${author?.id}") {
append(
StringUtil.getUsernameAnnotatedString(
context,
author?.name ?: "",
author?.nameShow
)
)
append(": ")
}
}
}
val contentStrings = content.renders.map { it.toAnnotationString() }
return userNameString + contentStrings.reduce { acc, annotatedString -> acc + annotatedString }
}

View File

@ -1,9 +1,8 @@
package com.huanchengfly.tieba.post.repository
import com.huanchengfly.tieba.post.api.TiebaApi
import com.huanchengfly.tieba.post.api.models.CommonResponse
import com.huanchengfly.tieba.post.api.models.protos.pbPage.PbPageResponse
import com.huanchengfly.tieba.post.api.retrofit.exception.TiebaApiException
import com.huanchengfly.tieba.post.api.retrofit.exception.TiebaUnknownException
import com.huanchengfly.tieba.post.ui.page.thread.ThreadPageFrom
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@ -38,7 +37,7 @@ object PbPageRepository {
|| response.data_.forum == null
|| response.data_.anti == null
) {
throw TiebaApiException(CommonResponse(-1, "未知错误"))
throw TiebaUnknownException
}
val userList = response.data_.user_list
val postList = response.data_.post_list.map {
@ -62,6 +61,14 @@ object PbPageRepository {
author = response.data_.thread.author,
from_forum = response.data_.forum,
tid = response.data_.thread.id,
sub_post_list = response.data_.first_floor_post.sub_post_list?.copy(
sub_post_list = response.data_.first_floor_post.sub_post_list.sub_post_list.map { subPost ->
subPost.copy(
author = subPost.author
?: userList.first { user -> user.id == subPost.author_id }
)
}
)
)
response.copy(

View File

@ -76,14 +76,10 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.withAnnotation
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.airbnb.lottie.compose.LottieAnimation
@ -100,7 +96,6 @@ import com.huanchengfly.tieba.post.api.models.protos.SimpleForum
import com.huanchengfly.tieba.post.api.models.protos.ThreadInfo
import com.huanchengfly.tieba.post.api.models.protos.User
import com.huanchengfly.tieba.post.api.models.protos.bawuType
import com.huanchengfly.tieba.post.api.models.protos.renders
import com.huanchengfly.tieba.post.api.retrofit.exception.getErrorMessage
import com.huanchengfly.tieba.post.arch.ImmutableHolder
import com.huanchengfly.tieba.post.arch.collectPartialAsState
@ -435,6 +430,10 @@ fun ThreadPage(
prop1 = ThreadUiState::contentRenders,
initial = persistentListOf()
)
val subPostContents by viewModel.uiState.collectPartialAsState(
prop1 = ThreadUiState::subPostContents,
initial = persistentListOf()
)
val author by viewModel.uiState.collectPartialAsState(
prop1 = ThreadUiState::author,
initial = null
@ -1035,6 +1034,7 @@ fun ThreadPage(
PostCard(
postHolder = item,
contentRenders = contentRenders[index],
subPostContents = subPostContents[index],
threadAuthorId = author?.get { id } ?: 0L,
blocked = blocked,
immersiveMode = isImmersiveMode,
@ -1285,11 +1285,11 @@ private fun BottomBar(
}
}
@OptIn(ExperimentalTextApi::class)
@Composable
fun PostCard(
postHolder: ImmutableHolder<Post>,
contentRenders: ImmutableList<PbContentRender>,
subPostContents: ImmutableList<AnnotatedString> = persistentListOf(),
threadAuthorId: Long = 0L,
blocked: Boolean = false,
immersiveMode: Boolean = false,
@ -1324,7 +1324,6 @@ fun PostCard(
postHolder.get { floor > 1 } && !immersiveMode
}
val paddingModifier = Modifier.padding(start = if (hasPadding) Sizes.Small + 8.dp else 0.dp)
val accentColor = ExtendedTheme.colors.accent
val author = postHolder.get { author!! }
val showTitle = remember(postHolder) {
post.title.isNotBlank() && post.floor <= 1
@ -1338,32 +1337,6 @@ fun PostCard(
val subPosts = remember(postHolder) {
post.sub_post_list?.sub_post_list?.toImmutableList() ?: persistentListOf()
}
val subPostContents = remember(key1 = subPosts, key2 = accentColor) {
subPosts.map { subPostList ->
val userNameString = buildAnnotatedString {
withStyle(
style = SpanStyle(
color = accentColor,
fontWeight = FontWeight.Bold
)
) {
withAnnotation("user", "${subPostList.author?.id}") {
append(
StringUtil.getUsernameAnnotatedString(
context,
subPostList.author?.name ?: "",
subPostList.author?.nameShow
)
)
append(": ")
}
}
}
val contentStrings = subPostList.content.renders.map { it.toAnnotationString() }
userNameString + contentStrings.reduce { acc, annotatedString -> acc + annotatedString }
}
}
Card(
header = {
if (!immersiveMode) {
@ -1429,7 +1402,7 @@ fun PostCard(
}
}
if (showSubPosts && post.sub_post_number > 0 && !immersiveMode) {
if (showSubPosts && post.sub_post_number > 0 && subPostContents.isNotEmpty() && !immersiveMode) {
Column(
modifier = Modifier
.fillMaxWidth()
@ -1513,7 +1486,7 @@ fun UserNameText(
"Bawu" to buildChipInlineContent(
bawuType ?: "",
color = ExtendedTheme.colors.accent,
backgroundColor = ExtendedTheme.colors.accent.copy(alpha = 0.25f)
backgroundColor = ExtendedTheme.colors.accent.copy(alpha = 0.1f)
),
"Lz" to buildChipInlineContent(stringResource(id = R.string.tip_lz)),
),

View File

@ -2,6 +2,7 @@ package com.huanchengfly.tieba.post.ui.page.thread
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.text.AnnotatedString
import com.huanchengfly.tieba.post.api.TiebaApi
import com.huanchengfly.tieba.post.api.models.AgreeBean
import com.huanchengfly.tieba.post.api.models.protos.Anti
@ -10,7 +11,9 @@ import com.huanchengfly.tieba.post.api.models.protos.SimpleForum
import com.huanchengfly.tieba.post.api.models.protos.ThreadInfo
import com.huanchengfly.tieba.post.api.models.protos.User
import com.huanchengfly.tieba.post.api.models.protos.contentRenders
import com.huanchengfly.tieba.post.api.models.protos.pbPage.PbPageResponse
import com.huanchengfly.tieba.post.api.models.protos.renders
import com.huanchengfly.tieba.post.api.models.protos.subPostContents
import com.huanchengfly.tieba.post.api.models.protos.updateAgreeStatus
import com.huanchengfly.tieba.post.api.models.protos.updateCollectStatus
import com.huanchengfly.tieba.post.api.retrofit.exception.TiebaUnknownException
@ -125,24 +128,10 @@ class ThreadViewModel @Inject constructor() :
threadId, page, postId, forumId, seeLz, sortType,
from = from.takeIf { it == ThreadPageFrom.FROM_STORE }.orEmpty()
)
.map { response ->
if (
response.data_?.page != null
&& response.data_.thread?.author != null
&& response.data_.forum != null
&& response.data_.anti != null
) {
val userList = response.data_.user_list
val postList = response.data_.post_list.map {
it.copy(
author = it.author
?: userList.first { user -> user.id == it.author_id },
from_forum = response.data_.forum,
tid = response.data_.thread.id,
)
}
val firstPost = postList.firstOrNull { it.floor == 1 }
?: response.data_.first_floor_post?.copy(author = response.data_.thread.author)
.map<PbPageResponse, ThreadPartialChange.Load> { response ->
if (response.data_?.page == null || response.data_.thread?.author == null || response.data_.forum == null || response.data_.anti == null) throw TiebaUnknownException
val postList = response.data_.post_list
val firstPost = response.data_.first_floor_post
val notFirstPosts = postList.filterNot { it.floor == 1 }
ThreadPartialChange.Load.Success(
response.data_.thread.title,
@ -163,11 +152,11 @@ class ThreadViewModel @Inject constructor() :
response.data_.page.has_prev != 0,
firstPost?.contentRenders,
notFirstPosts.map { it.contentRenders },
notFirstPosts.map { it.subPostContents }.toImmutableList(),
postId,
seeLz,
sortType,
)
} else ThreadPartialChange.Load.Failure(TiebaUnknownException)
}
.onStart { emit(ThreadPartialChange.Load.Start) }
.catch { emit(ThreadPartialChange.Load.Failure(it)) }
@ -206,6 +195,7 @@ class ThreadViewModel @Inject constructor() :
response.data_.page.has_prev != 0,
firstPost?.contentRenders ?: emptyList(),
notFirstPosts.map { it.contentRenders },
notFirstPosts.map { it.subPostContents }.toImmutableList(),
postId = 0,
seeLz,
sortType,
@ -241,7 +231,8 @@ class ThreadViewModel @Inject constructor() :
postIds + posts.map { it.id },
sortType
),
posts.map { it.contentRenders }
posts.map { it.contentRenders },
posts.map { it.subPostContents }.toImmutableList(),
)
} else ThreadPartialChange.LoadMore.Failure(-1, "未知错误")
}
@ -277,7 +268,8 @@ class ThreadViewModel @Inject constructor() :
response.data_.page.current_page,
response.data_.page.new_total_page,
response.data_.page.has_prev != 0,
posts.map { it.contentRenders }
posts.map { it.contentRenders },
posts.map { it.subPostContents }.toImmutableList(),
)
} else ThreadPartialChange.LoadPrevious.Failure(-1, "未知错误")
}
@ -537,6 +529,7 @@ sealed interface ThreadPartialChange : PartialChange<ThreadUiState> {
firstPostContentRenders = firstPostContentRenders?.toImmutableList()
?: oldState.firstPostContentRenders,
contentRenders = contentRenders.toImmutableList(),
subPostContents = subPostContents.toImmutableList(),
postId = postId,
seeLz = seeLz,
sortType = sortType,
@ -567,6 +560,7 @@ sealed interface ThreadPartialChange : PartialChange<ThreadUiState> {
val hasPrevious: Boolean,
val firstPostContentRenders: List<PbContentRender>?,
val contentRenders: List<ImmutableList<PbContentRender>>,
val subPostContents: List<ImmutableList<AnnotatedString>>,
val postId: Long = 0,
val seeLz: Boolean = false,
val sortType: Int = 0,
@ -596,6 +590,7 @@ sealed interface ThreadPartialChange : PartialChange<ThreadUiState> {
hasPrevious = hasPrevious,
firstPostContentRenders = firstPostContentRenders.toImmutableList(),
contentRenders = contentRenders.toImmutableList(),
subPostContents = subPostContents.toImmutableList(),
postId = postId,
seeLz = seeLz,
sortType = sortType,
@ -622,6 +617,7 @@ sealed interface ThreadPartialChange : PartialChange<ThreadUiState> {
val hasPrevious: Boolean,
val firstPostContentRenders: List<PbContentRender>,
val contentRenders: List<ImmutableList<PbContentRender>>,
val subPostContents: List<ImmutableList<AnnotatedString>>,
val postId: Long,
val seeLz: Boolean,
val sortType: Int,
@ -644,7 +640,8 @@ sealed interface ThreadPartialChange : PartialChange<ThreadUiState> {
totalPage = totalPage,
hasMore = hasMore,
nextPagePostId = nextPagePostId,
contentRenders = (oldState.contentRenders + contentRenders).toImmutableList()
contentRenders = (oldState.contentRenders + contentRenders).toImmutableList(),
subPostContents = (oldState.subPostContents + subPostContents).toImmutableList()
)
is Failure -> oldState.copy(isLoadingMore = false)
@ -661,6 +658,7 @@ sealed interface ThreadPartialChange : PartialChange<ThreadUiState> {
val hasMore: Boolean,
val nextPagePostId: Long,
val contentRenders: List<ImmutableList<PbContentRender>>,
val subPostContents: List<ImmutableList<AnnotatedString>>,
) : LoadMore()
data class Failure(
@ -680,7 +678,8 @@ sealed interface ThreadPartialChange : PartialChange<ThreadUiState> {
currentPageMin = currentPage,
totalPage = totalPage,
hasPrevious = hasPrevious,
contentRenders = (contentRenders + oldState.contentRenders).toImmutableList()
contentRenders = (contentRenders + oldState.contentRenders).toImmutableList(),
subPostContents = (subPostContents + oldState.subPostContents).toImmutableList()
)
is Failure -> oldState.copy(isRefreshing = false)
@ -696,6 +695,7 @@ sealed interface ThreadPartialChange : PartialChange<ThreadUiState> {
val totalPage: Int,
val hasPrevious: Boolean,
val contentRenders: List<ImmutableList<PbContentRender>>,
val subPostContents: List<ImmutableList<AnnotatedString>>,
) : LoadPrevious()
data class Failure(
@ -888,6 +888,7 @@ data class ThreadUiState(
val data: ImmutableList<PostItemData> = persistentListOf(),
val firstPostContentRenders: ImmutableList<PbContentRender> = persistentListOf(),
val contentRenders: ImmutableList<ImmutableList<PbContentRender>> = persistentListOf(),
val subPostContents: ImmutableList<ImmutableList<AnnotatedString>> = persistentListOf(),
val isImmersiveMode: Boolean = false,
) : UiState