From 9be00b177a11a356b8b5bf7c8bc54c67dd038797 Mon Sep 17 00:00:00 2001 From: HuanCheng65 <22636177+HuanCheng65@users.noreply.github.com> Date: Thu, 13 Jul 2023 18:32:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E7=89=88=E6=94=B6=E8=97=8F=20?= =?UTF-8?q?&=20=E7=82=B9=E8=B5=9E=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/huanchengfly/tieba/post/api/Utils.kt | 109 +++++++++++++----- .../tieba/post/api/interfaces/ITiebaApi.kt | 40 ++++++- .../api/interfaces/impls/MixedTiebaApiImpl.kt | 60 ++++++++-- .../post/api/models/NewCollectDataBean.kt | 11 ++ .../retrofit/interfaces/OfficialTiebaApi.kt | 72 +++++++++--- .../threadlist/ForumThreadListViewModel.kt | 1 - .../main/explore/concern/ConcernViewModel.kt | 2 +- .../personalized/PersonalizedViewModel.kt | 2 +- app/src/main/protos/Page.proto | 6 + app/src/main/protos/ThreadInfo.proto | 2 + 10 files changed, 242 insertions(+), 63 deletions(-) create mode 100644 app/src/main/java/com/huanchengfly/tieba/post/api/models/NewCollectDataBean.kt diff --git a/app/src/main/java/com/huanchengfly/tieba/post/api/Utils.kt b/app/src/main/java/com/huanchengfly/tieba/post/api/Utils.kt index 4894e828..8a1ab56c 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/api/Utils.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/api/Utils.kt @@ -90,6 +90,46 @@ fun ThreadInfo.updateAgreeStatus( ) } +fun ThreadInfo.updateCollectStatus( + newStatus: Int, + markPostId: Long +) = if (collectStatus != newStatus) { + this.copy( + collectStatus = newStatus, + collectMarkPid = markPostId.toString() + ) +} else { + this +} + +fun Post.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 + ) + ) + } else { + copy( + agree = agree.copy( + agreeNum = agree.agreeNum - 1, + diffAgreeNum = agree.diffAgreeNum - 1, + hasAgree = 0 + ) + ) + } + } else { + this + } +} else { + this +} + private val PbContent.picUrl: String get() = ImageUtil.getUrl( @@ -123,12 +163,7 @@ val List.renders: List forEach { when (it.type) { 0, 9, 27 -> { - val lastRender = renders.lastOrNull() - if (lastRender is TextContentRender) { - renders.removeLast() - renders.add(lastRender + it.text) - } else - renders.add(TextContentRender(it.text)) + renders.appendText(it.text) } 1 -> { @@ -149,12 +184,7 @@ val List.renders: List } } } - val lastRender = renders.lastOrNull() - if (lastRender is TextContentRender) { - renders.removeLast() - renders.add(lastRender + text) - } else - renders.add(TextContentRender(text)) + renders.appendText(text) } 2 -> { @@ -163,12 +193,7 @@ val List.renders: List it.c ) val emoticonText = "#(${it.c})".emoticonString - val lastRender = renders.lastOrNull() - if (lastRender is TextContentRender) { - renders.removeLast() - renders.add(lastRender + emoticonText) - } else - renders.add(TextContentRender(emoticonText)) + renders.appendText(emoticonText) } 3 -> { @@ -189,7 +214,6 @@ val List.renders: List 4 -> { val text = buildAnnotatedString { - appendInlineContent("user_icon", alternateText = "🧑") withAnnotation(tag = "user", annotation = "${it.uid}") { withStyle( SpanStyle( @@ -205,12 +229,43 @@ val List.renders: List } } } - val lastRender = renders.lastOrNull() - if (lastRender is TextContentRender) { - renders.removeLast() - renders.add(lastRender + text) - } else - renders.add(TextContentRender(text)) + renders.appendText(text) + } + + 5 -> { + if (it.src.isNotBlank()) { + val width = it.bsize.split(",")[0].toInt() + val height = it.bsize.split(",")[1].toInt() + renders.add( + VideoContentRender( + videoUrl = it.link, + picUrl = it.src, + webUrl = it.text, + width = width, + height = height + ) + ) + } else { + val text = buildAnnotatedString { + appendInlineContent("video_icon", alternateText = "🎥") + withAnnotation(tag = "url", annotation = it.text) { + withStyle( + SpanStyle( + color = Color( + ThemeUtils.getColorByAttr( + App.INSTANCE, + R.attr.colorPrimary + ) + ) + ) + ) { + append(App.INSTANCE.getString(R.string.tag_video)) + append(it.text) + } + } + } + renders.appendText(text) + } } 20 -> { @@ -234,7 +289,7 @@ val List.renders: List return renders } -val Post.contentRenders: List +val Post.contentRenders: ImmutableList get() { val renders = content.renders @@ -252,5 +307,5 @@ val Post.contentRenders: List } else it } - return renders + return renders.toImmutableList() } \ No newline at end of file diff --git a/app/src/main/java/com/huanchengfly/tieba/post/api/interfaces/ITiebaApi.kt b/app/src/main/java/com/huanchengfly/tieba/post/api/interfaces/ITiebaApi.kt index f9edd12e..188f642e 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/api/interfaces/ITiebaApi.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/api/interfaces/ITiebaApi.kt @@ -96,8 +96,9 @@ interface ITiebaApi { */ fun opAgreeFlow( threadId: String, - postId: String, - opType: Int + opType: Int, + postId: String? = null, + isSubPost: Boolean = false, ): Flow /** @@ -527,6 +528,20 @@ interface ITiebaApi { tbs: String ): Call + /** + * 移除收藏 + * + * **需登录** + * + * @param threadId 贴子 ID + * @param tbs tbs + */ + fun removeStoreFlow( + threadId: Long, + forumId: Long, + tbs: String? + ): Flow + /** * 移除收藏 * @@ -553,6 +568,19 @@ interface ITiebaApi { tbs: String ): Call + /** + * 添加/更新收藏 + * + * **需登录** + * + * @param threadId 贴子 ID + * @param postId 收藏到的回复 ID + */ + fun addStoreFlow( + threadId: Long, + postId: Long + ): Flow + /** * 回复我的消息列表 * @@ -1231,11 +1259,17 @@ interface ITiebaApi { * 贴子页(Flow) * * @param threadId 贴子 ID + * @param page 页码(从 1 开始) */ fun pbPageFlow( threadId: Long, - page: Int, + page: Int = 0, + postId: Long = 0L, + seeLz: Boolean = false, + back: Boolean = false, sortType: Int = 0, forumId: Long? = null, + stType: String = "", + mark: Int = 0, ): Flow } \ No newline at end of file diff --git a/app/src/main/java/com/huanchengfly/tieba/post/api/interfaces/impls/MixedTiebaApiImpl.kt b/app/src/main/java/com/huanchengfly/tieba/post/api/interfaces/impls/MixedTiebaApiImpl.kt index 7556ac4a..6cce2906 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/api/interfaces/impls/MixedTiebaApiImpl.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/api/interfaces/impls/MixedTiebaApiImpl.kt @@ -31,6 +31,7 @@ import com.huanchengfly.tieba.post.api.models.LoginBean import com.huanchengfly.tieba.post.api.models.MSignBean import com.huanchengfly.tieba.post.api.models.MessageListBean import com.huanchengfly.tieba.post.api.models.MsgBean +import com.huanchengfly.tieba.post.api.models.NewCollectDataBean import com.huanchengfly.tieba.post.api.models.PersonalizedBean import com.huanchengfly.tieba.post.api.models.PicPageBean import com.huanchengfly.tieba.post.api.models.Profile @@ -159,10 +160,16 @@ object MixedTiebaApiImpl : ITiebaApi { override fun opAgreeFlow( threadId: String, - postId: String, - opType: Int + opType: Int, + postId: String?, + isSubPost: Boolean ): Flow = - RetrofitTiebaApi.MINI_TIEBA_API.agreeFlow(postId, threadId, op_type = opType) + RetrofitTiebaApi.OFFICIAL_TIEBA_API.agreeFlow( + threadId, + postId, + opType = opType, + objType = if (postId == null) 3 else if (isSubPost) 2 else 1 + ) override fun disagreeFlow( threadId: String, @@ -393,6 +400,17 @@ object MixedTiebaApiImpl : ITiebaApi { override fun removeStore(threadId: String, tbs: String): Call = RetrofitTiebaApi.NEW_TIEBA_API.removeStore(threadId, tbs) + override fun removeStoreFlow( + threadId: Long, + forumId: Long, + tbs: String? + ): Flow = + RetrofitTiebaApi.OFFICIAL_TIEBA_API.removeStoreFlow( + threadId.toString(), + forumId.toString(), + tbs ?: AccountUtil.getLoginInfo()!!.tbs + ) + override fun removeStoreFlow(threadId: String): Flow = RetrofitTiebaApi.OFFICIAL_TIEBA_API.removeStoreFlow(threadId) @@ -405,9 +423,22 @@ object MixedTiebaApiImpl : ITiebaApi { "0", "0" ) - ).toJson(), tbs + ).toJson(), + tbs ) + override fun addStoreFlow(threadId: Long, postId: Long): Flow = + RetrofitTiebaApi.OFFICIAL_TIEBA_API.addStoreFlow( + listOf( + NewCollectDataBean( + threadId.toString(), + postId.toString(), + status = 1 + ) + ).toJson() + ) + + override fun replyMe(page: Int): Call = RetrofitTiebaApi.NEW_TIEBA_API.replyMe(page) @@ -1009,8 +1040,13 @@ object MixedTiebaApiImpl : ITiebaApi { override fun pbPageFlow( threadId: Long, page: Int, + postId: Long, + seeLz: Boolean, + back: Boolean, sortType: Int, - forumId: Long? + forumId: Long?, + stType: String, + mark: Int, ): Flow { return RetrofitTiebaApi.OFFICIAL_PROTOBUF_TIEBA_V12_API.pbPageFlow( buildProtobufRequestBody( @@ -1018,18 +1054,23 @@ object MixedTiebaApiImpl : ITiebaApi { PbPageRequestData( common = buildCommonRequest(clientVersion = ClientVersion.TIEBA_V12), kz = threadId, + pid = postId, + pn = page, + r = sortType, + lz = if (seeLz) 1 else 0, + forum_id = forumId ?: 0, ad_param = com.huanchengfly.tieba.post.api.models.protos.pbPage.AdParam( load_count = 0, refresh_count = 1, is_req_ad = 1 ), + mark = mark, app_pos = buildAppPosInfo(), - back = 0, + back = if (back) 1 else 0, banner = 1, broadcast_id = 0, floor_rn = 4, floor_sort_type = 1, - forum_id = forumId ?: 0, from_push = 0, from_smart_frs = 0, immersion_video_comment_source = 0, @@ -1043,17 +1084,14 @@ object MixedTiebaApiImpl : ITiebaApi { obj_source = "", ori_ugc_type = 0, pb_rn = 0, - pid = 0, - pn = page, q_type = 1, - r = sortType, rn = 15, s_model = 0, scr_dip = App.ScreenInfo.DENSITY.toDouble(), scr_h = getScreenHeight(), scr_w = getScreenWidth(), source_type = 2, - st_type = "personalize_page", + st_type = stType, thread_type = 0, weipost = 0, with_floor = 1 diff --git a/app/src/main/java/com/huanchengfly/tieba/post/api/models/NewCollectDataBean.kt b/app/src/main/java/com/huanchengfly/tieba/post/api/models/NewCollectDataBean.kt new file mode 100644 index 00000000..cbaaa976 --- /dev/null +++ b/app/src/main/java/com/huanchengfly/tieba/post/api/models/NewCollectDataBean.kt @@ -0,0 +1,11 @@ +package com.huanchengfly.tieba.post.api.models + +import com.google.gson.annotations.SerializedName + +data class NewCollectDataBean( + @SerializedName("tid") + val threadId: String, + @SerializedName("pid") + val postId: String, + val status: Int +) \ No newline at end of file diff --git a/app/src/main/java/com/huanchengfly/tieba/post/api/retrofit/interfaces/OfficialTiebaApi.kt b/app/src/main/java/com/huanchengfly/tieba/post/api/retrofit/interfaces/OfficialTiebaApi.kt index 9aa9a391..11f891f0 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/api/retrofit/interfaces/OfficialTiebaApi.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/api/retrofit/interfaces/OfficialTiebaApi.kt @@ -327,25 +327,6 @@ interface OfficialTiebaApi { @Field("user_id") user_id: String? = AccountUtil.getUid(), ): Flow - @POST("/c/c/post/rmstore") - @FormUrlEncoded - @Headers( - "${Header.FORCE_LOGIN}: ${Header.FORCE_LOGIN_TRUE}", - "${Header.COOKIE}: ka=open", - "${Header.DROP_HEADERS}: ${Header.CHARSET},${Header.CLIENT_TYPE}", - "${Header.NO_COMMON_PARAMS}: ${Param.OAID}", - ) - fun removeStoreFlow( - @Field("tid") threadId: String, - @Field("tbs") tbs: String = AccountUtil.getLoginInfo()!!.tbs, - @Field("stoken") stoken: String = AccountUtil.getSToken()!!, - @Field("user_id") user_id: String? = AccountUtil.getUid(), - @Field("fid") fid: String = "null", - @Field("_client_version") client_version: String = "11.10.8.6", - @retrofit2.http.Header(Header.USER_AGENT) user_agent: String = "bdtb for Android $client_version", - @retrofit2.http.Header("client_user_token") client_user_token: String? = user_id, - ): Flow - @Headers( "${Header.COOKIE}: ka=open", "${Header.DROP_HEADERS}: ${Header.CHARSET},${Header.CLIENT_TYPE}", @@ -414,4 +395,57 @@ interface OfficialTiebaApi { @Field("stoken") sToken: String? = AccountUtil.getSToken(), @retrofit2.http.Header("client_user_token") client_user_token: String? = AccountUtil.getUid(), ): Flow + + @POST("/c/c/post/rmstore") + @FormUrlEncoded + @Headers( + "${Header.FORCE_LOGIN}: ${Header.FORCE_LOGIN_TRUE}", + "${Header.COOKIE}: ka=open", + "${Header.DROP_HEADERS}: ${Header.CHARSET},${Header.CLIENT_TYPE}", + "${Header.NO_COMMON_PARAMS}: ${Param.OAID}", + ) + fun removeStoreFlow( + @Field("tid") threadId: String, + @Field("fid") forumId: String = "null", + @Field("tbs") tbs: String = AccountUtil.getLoginInfo()!!.tbs, + @Field("stoken") stoken: String = AccountUtil.getSToken()!!, + @Field("user_id") user_id: String? = AccountUtil.getUid(), + @retrofit2.http.Header("client_user_token") client_user_token: String? = user_id, + ): Flow + + @POST("/c/c/post/addstore") + @FormUrlEncoded + @Headers( + "${Header.FORCE_LOGIN}: ${Header.FORCE_LOGIN_TRUE}", + "${Header.COOKIE}: ka=open", + "${Header.DROP_HEADERS}: ${Header.CHARSET},${Header.CLIENT_TYPE}", + "${Header.NO_COMMON_PARAMS}: ${Param.OAID}", + ) + fun addStoreFlow( + @Field("data") data: String, + @Field("stoken") stoken: String = AccountUtil.getSToken()!!, + @retrofit2.http.Header("client_user_token") client_user_token: String? = AccountUtil.getUid(), + ): Flow + + @Headers( + "${Header.FORCE_LOGIN}: ${Header.FORCE_LOGIN_TRUE}", + "${Header.COOKIE}: ka=open", + "${Header.DROP_HEADERS}: ${Header.CHARSET},${Header.CLIENT_TYPE}", + "${Header.NO_COMMON_PARAMS}: ${Param.OAID}", + ) + @POST("/c/c/agree/opAgree") + @FormUrlEncoded + fun agreeFlow( + @Field("thread_id") threadId: String, + @Field("post_id") postId: String? = null, + @Field("op_type") opType: Int = 0, + @Field("obj_type") objType: Int = 1, + @Field("agree_type") agreeType: Int = 2, + @retrofit2.http.Header("client_user_token") client_user_token: String? = AccountUtil.getUid(), + @Field("cuid_gid") cuid_gid: String = "", + @Field("forum_id") forumId: String = "", + @Field("personalized_rec_switch") personalizedRecSwitch: Int = 1, + @Field("tbs") tbs: String = AccountUtil.getLoginInfo()!!.tbs, + @Field("stoken") stoken: String = AccountUtil.getSToken()!! + ): Flow } \ No newline at end of file diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/forum/threadlist/ForumThreadListViewModel.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/forum/threadlist/ForumThreadListViewModel.kt index 103a3bc8..b9dbe4c9 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/forum/threadlist/ForumThreadListViewModel.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/forum/threadlist/ForumThreadListViewModel.kt @@ -203,7 +203,6 @@ private class ForumThreadListPartialChangeProducer(val type: ForumThreadListType private fun ForumThreadListUiIntent.Agree.producePartialChange(): Flow = TiebaApi.getInstance().opAgreeFlow( threadId.toString(), - postId.toString(), hasAgree, ).map { ForumThreadListPartialChange.Agree.Success( diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/explore/concern/ConcernViewModel.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/explore/concern/ConcernViewModel.kt index 846a5e05..1f73a2d8 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/explore/concern/ConcernViewModel.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/explore/concern/ConcernViewModel.kt @@ -79,7 +79,7 @@ class ConcernViewModel @Inject constructor() : private fun ConcernUiIntent.Agree.producePartialChange(): Flow = TiebaApi.getInstance().opAgreeFlow( - threadId.toString(), postId.toString(), hasAgree, + threadId.toString(), hasAgree ).map { ConcernPartialChange.Agree.Success(threadId, hasAgree xor 1) } .catch { emit(ConcernPartialChange.Agree.Failure(threadId, hasAgree, it)) } .onStart { emit(ConcernPartialChange.Agree.Start(threadId, hasAgree xor 1)) } diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/explore/personalized/PersonalizedViewModel.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/explore/personalized/PersonalizedViewModel.kt index dd3e6f3d..1dddcae6 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/explore/personalized/PersonalizedViewModel.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/main/explore/personalized/PersonalizedViewModel.kt @@ -100,7 +100,7 @@ class PersonalizedViewModel @Inject constructor() : private fun PersonalizedUiIntent.Agree.producePartialChange(): Flow = TiebaApi.getInstance().opAgreeFlow( - threadId.toString(), postId.toString(), hasAgree, + threadId.toString(), hasAgree, ).map { PersonalizedPartialChange.Agree.Success( threadId, diff --git a/app/src/main/protos/Page.proto b/app/src/main/protos/Page.proto index d0c8d9ad..aea61338 100644 --- a/app/src/main/protos/Page.proto +++ b/app/src/main/protos/Page.proto @@ -13,4 +13,10 @@ message Page { int32 has_more = 6; int32 has_prev = 7; int32 cur_good_id = 8; + int32 req_num = 9; + int32 pnum = 10; + int32 tnum = 11; + int32 total_num = 12; + int32 lz_total_floor = 13; + int32 new_total_page = 14; } \ No newline at end of file diff --git a/app/src/main/protos/ThreadInfo.proto b/app/src/main/protos/ThreadInfo.proto index eab1e33d..446c6877 100644 --- a/app/src/main/protos/ThreadInfo.proto +++ b/app/src/main/protos/ThreadInfo.proto @@ -36,8 +36,10 @@ message ThreadInfo { int32 createTime = 45; int32 collectStatus = 50; string collectMarkPid = 51; + int64 post_id = 52; int32 isMemberTop = 54; int64 authorId = 56; + string pids = 61; optional VideoInfo videoInfo = 79; repeated PbContent richTitle = 111; repeated PbContent richAbstract = 112;