diff --git a/app/src/main/java/com/huanchengfly/tieba/post/api/models/protos/Extensions.kt b/app/src/main/java/com/huanchengfly/tieba/post/api/models/protos/Extensions.kt index 27b5a71c..cf7c88c9 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/api/models/protos/Extensions.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/api/models/protos/Extensions.kt @@ -160,6 +160,36 @@ fun SubPostList.updateAgreeStatus( this } +fun PostInfoList.updateAgreeStatus( + hasAgree: Int, +) = if (agree != null) { + if (hasAgree != agree.hasAgree) { + if (hasAgree == 1) { + copy( + agree = agree.copy( + agreeNum = agree.agreeNum + 1, + diffAgreeNum = agree.diffAgreeNum + 1, + hasAgree = 1 + ), + agree_num = agree_num + 1 + ) + } else { + copy( + agree = agree.copy( + agreeNum = agree.agreeNum - 1, + diffAgreeNum = agree.diffAgreeNum - 1, + hasAgree = 0 + ), + agree_num = agree_num - 1 + ) + } + } else { + this + } +} else { + this +} + private val PbContent.picUrl: String get() = ImageUtil.getUrl( diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/user/post/UserPostPage.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/user/post/UserPostPage.kt index fdee09a4..671f91b9 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/user/post/UserPostPage.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/user/post/UserPostPage.kt @@ -214,7 +214,15 @@ fun UserPostPage( ) ) }, - onAgreeItem = {}, + onAgreeItem = { + viewModel.send( + UserPostUiIntent.Agree( + it.thread_id, + it.post_id, + it.agree?.hasAgree ?: 0 + ) + ) + }, onClickReply = { navigator.navigate( ThreadPageDestination( diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/user/post/UserPostViewModel.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/user/post/UserPostViewModel.kt index ab4473c5..8161eaeb 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/user/post/UserPostViewModel.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/user/post/UserPostViewModel.kt @@ -1,9 +1,15 @@ package com.huanchengfly.tieba.post.ui.page.user.post +import com.huanchengfly.tieba.post.App +import com.huanchengfly.tieba.post.R import com.huanchengfly.tieba.post.api.TiebaApi +import com.huanchengfly.tieba.post.api.models.AgreeBean import com.huanchengfly.tieba.post.api.models.protos.PostInfoList +import com.huanchengfly.tieba.post.api.models.protos.updateAgreeStatus import com.huanchengfly.tieba.post.api.models.protos.userPost.UserPostResponse +import com.huanchengfly.tieba.post.api.retrofit.exception.getErrorMessage import com.huanchengfly.tieba.post.arch.BaseViewModel +import com.huanchengfly.tieba.post.arch.CommonUiEvent import com.huanchengfly.tieba.post.arch.ImmutableHolder import com.huanchengfly.tieba.post.arch.PartialChange import com.huanchengfly.tieba.post.arch.PartialChangeProducer @@ -33,6 +39,18 @@ class UserPostViewModel @Inject constructor() : override fun createPartialChangeProducer(): PartialChangeProducer = UserPostPartialChangeProducer + override fun dispatchEvent(partialChange: UserPostPartialChange): UiEvent? = + when (partialChange) { + is UserPostPartialChange.Agree.Failure -> CommonUiEvent.Toast( + App.INSTANCE.getString( + R.string.toast_agree_failed, + partialChange.error.getErrorMessage() + ) + ) + + else -> null + } + private object UserPostPartialChangeProducer : PartialChangeProducer { @OptIn(ExperimentalCoroutinesApi::class) @@ -42,6 +60,8 @@ class UserPostViewModel @Inject constructor() : .flatMapConcat { it.toPartialChangeFlow() }, intentFlow.filterIsInstance() .flatMapConcat { it.toPartialChangeFlow() }, + intentFlow.filterIsInstance() + .flatMapConcat { it.toPartialChangeFlow() } ) private fun UserPostUiIntent.Refresh.toPartialChangeFlow(): Flow = @@ -74,6 +94,25 @@ class UserPostViewModel @Inject constructor() : } .onStart { emit(UserPostPartialChange.LoadMore.Start) } .catch { emit(UserPostPartialChange.LoadMore.Failure(it)) } + + private fun UserPostUiIntent.Agree.toPartialChangeFlow(): Flow = + TiebaApi.getInstance() + .opAgreeFlow( + threadId.toString(), postId.toString(), hasAgree, objType = 3 + ) + .map { + UserPostPartialChange.Agree.Success(threadId, postId, hasAgree xor 1) + } + .onStart { + emit( + UserPostPartialChange.Agree.Start( + threadId, + postId, + hasAgree xor 1 + ) + ) + } + .catch { emit(UserPostPartialChange.Agree.Failure(threadId, postId, hasAgree, it)) } } } @@ -88,6 +127,12 @@ sealed interface UserPostUiIntent : UiIntent { val isThread: Boolean, val page: Int, ) : UserPostUiIntent + + data class Agree( + val threadId: Long, + val postId: Long, + val hasAgree: Int, + ) : UserPostUiIntent } sealed interface UserPostPartialChange : PartialChange { @@ -168,6 +213,75 @@ sealed interface UserPostPartialChange : PartialChange { val error: Throwable, ) : LoadMore() } + + sealed class Agree : UserPostPartialChange { + private fun List>.updateAgreeStatus( + threadId: Long, + postId: Long, + hasAgree: Int, + ): ImmutableList> { + return map { + val (postInfo) = it + if (postInfo.thread_id == threadId && postInfo.post_id == postId) { + postInfo.updateAgreeStatus(hasAgree) + } else { + postInfo + } + }.wrapImmutable() + } + + override fun reduce(oldState: UserPostUiState): UserPostUiState = + when (this) { + is Start -> { + oldState.copy( + posts = oldState.posts.updateAgreeStatus( + threadId, + postId, + hasAgree + ) + ) + } + + is Success -> { + oldState.copy( + posts = oldState.posts.updateAgreeStatus( + threadId, + postId, + hasAgree + ) + ) + } + + is Failure -> { + oldState.copy( + posts = oldState.posts.updateAgreeStatus( + threadId, + postId, + hasAgree + ) + ) + } + } + + data class Start( + val threadId: Long, + val postId: Long, + val hasAgree: Int, + ) : Agree() + + data class Success( + val threadId: Long, + val postId: Long, + val hasAgree: Int, + ) : Agree() + + data class Failure( + val threadId: Long, + val postId: Long, + val hasAgree: Int, + val error: Throwable, + ) : Agree() + } } data class UserPostUiState(