diff --git a/app/src/main/java/com/xiaoyv/bangumi/ui/discover/group/topic/GroupTopicsActivity.kt b/app/src/main/java/com/xiaoyv/bangumi/ui/discover/group/topic/GroupTopicsActivity.kt index 8a4a6eff..819cfeb4 100644 --- a/app/src/main/java/com/xiaoyv/bangumi/ui/discover/group/topic/GroupTopicsActivity.kt +++ b/app/src/main/java/com/xiaoyv/bangumi/ui/discover/group/topic/GroupTopicsActivity.kt @@ -38,31 +38,39 @@ class GroupTopicsActivity : BaseListActivity) { + binding.toolbar.title = viewModel.groupName + + invalidateMenu() + } + override fun onCreateOptionsMenu(menu: Menu): Boolean { - menu.add("发表") - .setIcon(CommonDrawable.ic_post_add) - .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS) - .setOnMenuItemClickListener { - if (UserHelper.isLogin.not()) { - RouteHelper.jumpLogin() - return@setOnMenuItemClickListener true - } - RouteHelper.jumpPostTopic( - PostAttach( - id = viewModel.groupId, - image = "", - title = viewModel.groupName, - type = TopicType.TYPE_GROUP + // 只有在小组详情内才显示发布按钮 + if (viewModel.isQueryMyTopic.not()) { + menu.add("发表") + .setIcon(CommonDrawable.ic_post_add) + .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS) + .setOnMenuItemClickListener { + if (UserHelper.isLogin.not()) { + RouteHelper.jumpLogin() + return@setOnMenuItemClickListener true + } + + // 构建小组话题发布的附件模型,跳转到发布小组话题 + RouteHelper.jumpPostTopic( + PostAttach( + id = viewModel.groupId, + image = "", + title = viewModel.groupName, + type = TopicType.TYPE_GROUP + ) ) - ) - true - } + true + } + } return super.onCreateOptionsMenu(menu) } - override fun onListDataFinish(list: List) { - binding.toolbar.title = viewModel.groupName - } override fun onCreateContentAdapter(): BaseQuickDiffBindingAdapter { return GroupTopicsAdapter() diff --git a/app/src/main/java/com/xiaoyv/bangumi/ui/discover/group/topic/GroupTopicsViewModel.kt b/app/src/main/java/com/xiaoyv/bangumi/ui/discover/group/topic/GroupTopicsViewModel.kt index affa9eb7..84c2f8aa 100644 --- a/app/src/main/java/com/xiaoyv/bangumi/ui/discover/group/topic/GroupTopicsViewModel.kt +++ b/app/src/main/java/com/xiaoyv/bangumi/ui/discover/group/topic/GroupTopicsViewModel.kt @@ -17,16 +17,16 @@ class GroupTopicsViewModel : BaseListViewModel() { internal var groupName = "" /** - * 我的回复和发布也是复用改类的逻辑 + * 我的回复和发布页面,复用此类的逻辑 */ - private val isMyTopic: Boolean + val isQueryMyTopic: Boolean get() = groupId == GlobalConfig.GROUP_MY_REPLY_TOPIC || groupId == GlobalConfig.GROUP_MY_SEND_TOPIC override suspend fun onRequestListImpl(): List { require(groupId.isNotBlank()) { "小组不存在" } // 如果是查询我的回复或发布 - if (isMyTopic) { + if (isQueryMyTopic) { val (groupName, topics) = BgmApiManager.bgmWebApi .queryUserTopicList(type = groupId, page = current) .parserGroupTopics(groupId) diff --git a/app/src/main/java/com/xiaoyv/bangumi/ui/feature/post/topic/PostTopicViewModel.kt b/app/src/main/java/com/xiaoyv/bangumi/ui/feature/post/topic/PostTopicViewModel.kt index 65de08f6..a997ef4c 100644 --- a/app/src/main/java/com/xiaoyv/bangumi/ui/feature/post/topic/PostTopicViewModel.kt +++ b/app/src/main/java/com/xiaoyv/bangumi/ui/feature/post/topic/PostTopicViewModel.kt @@ -5,6 +5,7 @@ import com.xiaoyv.bangumi.ui.feature.post.BasePostViewModel import com.xiaoyv.blueprint.kts.launchUI import com.xiaoyv.common.api.BgmApiManager import com.xiaoyv.common.api.parser.entity.CreatePostEntity +import com.xiaoyv.common.api.parser.impl.parserGroupDetail import com.xiaoyv.common.api.parser.impl.parserTopicSendResult import com.xiaoyv.common.config.GlobalConfig import com.xiaoyv.common.config.annotation.TopicType @@ -12,8 +13,6 @@ import com.xiaoyv.common.helper.UserHelper import com.xiaoyv.widget.kts.errorMsg import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.MultipartBody /** * Class: [PostTopicViewModel] @@ -30,9 +29,12 @@ open class PostTopicViewModel : BasePostViewModel() { internal var targetEditId = "" - internal val targetId + private val targetId get() = requireAttach?.id.orEmpty() + /** + * 发布话题,这里目前都是小组类型,章节和人物暂时没有支持 + */ @TopicType internal val topicType: String get() = requireAttach?.type.orEmpty() @@ -49,15 +51,23 @@ open class PostTopicViewModel : BasePostViewModel() { onPostTopicId.value = (null to it.errorMsg) }, block = { + // 自己的回复不能发布 require(targetId.isNotBlank() && targetId != GlobalConfig.GROUP_MY_SEND_TOPIC && targetId != GlobalConfig.GROUP_MY_REPLY_TOPIC) { "请先选择一个讨论的条目或小组" } + // 判断是否为小组的ID,是的话需要获取数字小组 ID 才能发布 + var sendTargetId = targetId + if (topicType == TopicType.TYPE_GROUP) { + sendTargetId = fetchGroupNumberIdById(targetId) + } + + // 发布 onPostTopicId.value = withContext(Dispatchers.IO) { val map = generateMap(entity) val topicId = BgmApiManager.bgmWebApi - .postCreateTopic(topicType, targetId, map) + .postCreateTopic(topicType, sendTargetId, map) .parserTopicSendResult() if (topicId.isNotBlank()) { @@ -74,7 +84,9 @@ open class PostTopicViewModel : BasePostViewModel() { } - + /** + * 构建发布参数 + */ private fun generateMap(entity: CreatePostEntity): Map { if (entity.title.isBlank()) { throw IllegalArgumentException("请输入标题") @@ -85,7 +97,7 @@ open class PostTopicViewModel : BasePostViewModel() { require(UserHelper.formHash.isNotBlank()) { "啊咧咧?参数丢失出错了!" } - return mapOf( + return mapOf( "formhash" to UserHelper.formHash, "title" to entity.title, "content" to entity.content, @@ -93,4 +105,24 @@ open class PostTopicViewModel : BasePostViewModel() { "submit" to "加上去" ) } + + /** + * 获取小组的数字ID,用于发布话题,登录后才可以获取 + */ + private suspend fun fetchGroupNumberIdById(groupId: String): String { + return withContext(Dispatchers.IO) { + require(UserHelper.isLogin) { "你还没有登录呢" } + val groupDetail = BgmApiManager.bgmWebApi.queryGroupDetail(groupId) + .parserGroupDetail(groupId) + + // 半公开小组,必须加入才可以发言 + if (groupDetail.isJoin.not() && groupDetail.groupNumberId.isBlank()) { + throw IllegalArgumentException("小组是半公开小组,只有小组成员才能发言") + } + + require(groupDetail.groupNumberId.isNotBlank()) { "小组ID查询失败" } + + return@withContext groupDetail.groupNumberId + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/xiaoyv/bangumi/ui/media/detail/chapter/MediaChapterAdapter.kt b/app/src/main/java/com/xiaoyv/bangumi/ui/media/detail/chapter/MediaChapterAdapter.kt index 19efef7c..83193ad2 100644 --- a/app/src/main/java/com/xiaoyv/bangumi/ui/media/detail/chapter/MediaChapterAdapter.kt +++ b/app/src/main/java/com/xiaoyv/bangumi/ui/media/detail/chapter/MediaChapterAdapter.kt @@ -4,8 +4,12 @@ import androidx.core.view.isVisible import androidx.recyclerview.widget.DiffUtil import com.xiaoyv.bangumi.databinding.FragmentMediaChapterItemBinding import com.xiaoyv.common.api.parser.entity.MediaChapterEntity +import com.xiaoyv.common.kts.CommonColor +import com.xiaoyv.common.kts.GoogleAttr +import com.xiaoyv.common.kts.tint import com.xiaoyv.widget.binder.BaseQuickBindingHolder import com.xiaoyv.widget.binder.BaseQuickDiffBindingAdapter +import com.xiaoyv.widget.kts.getAttrColor /** * Class: [MediaChapterAdapter] @@ -22,19 +26,27 @@ class MediaChapterAdapter : BaseQuickDiffBindingAdapter() { override fun areItemsTheSame( oldItem: MediaChapterEntity, - newItem: MediaChapterEntity + newItem: MediaChapterEntity, ): Boolean { return oldItem.id == newItem.id } override fun areContentsTheSame( oldItem: MediaChapterEntity, - newItem: MediaChapterEntity + newItem: MediaChapterEntity, ): Boolean { return oldItem == newItem } diff --git a/app/src/main/res/layout/fragment_media_action.xml b/app/src/main/res/layout/fragment_media_action.xml index 2c92aa0e..4c7ffcba 100644 --- a/app/src/main/res/layout/fragment_media_action.xml +++ b/app/src/main/res/layout/fragment_media_action.xml @@ -140,7 +140,7 @@ android:background="@drawable/ui_shape_rectangle_corner_6" android:backgroundTint="?attr/colorSurfaceContainer" android:gravity="top" - android:hint="吐槽" + android:hint="吐槽:公开情况下该内容会显示在详情页面最下方" android:minLines="4" android:paddingHorizontal="@dimen/ui_size_12" android:paddingVertical="@dimen/ui_size_12" diff --git a/app/src/main/res/layout/fragment_media_chapter_item.xml b/app/src/main/res/layout/fragment_media_chapter_item.xml index 0dbc0e4b..eca9249c 100644 --- a/app/src/main/res/layout/fragment_media_chapter_item.xml +++ b/app/src/main/res/layout/fragment_media_chapter_item.xml @@ -11,14 +11,16 @@ @@ -32,15 +34,30 @@ app:layout_constraintTop_toBottomOf="@+id/title_native" tools:text="@tools:sample/lorem" /> + + * https://lain.bgm.tv/pic/photo/g/41/30/823739_i8sWx.jpg */ -fun String.optImageUrl(large: Boolean = true, largest: Boolean = false): String { +fun String.optImageUrl( + large: Boolean = true, + largest: Boolean = false, + default: Boolean = true, +): String { val imageUrl = when { startsWith("//") -> "https:$this" startsWith("/") -> "${BgmApiManager.URL_BASE_WEB}$this" @@ -64,7 +68,7 @@ fun String.optImageUrl(large: Boolean = true, largest: Boolean = false): String .replace("https://(.*?)/no_icon.jpg".toRegex(), defaultImage) .replace("https://(.*?)/no_icon_subject.png".toRegex(), defaultImage) .replace("https://(.*?)/icon.jpg".toRegex(), defaultImage) - .ifBlank { defaultImage } + .ifBlank { if (default) defaultImage else "" } } fun String?.parseCount(): Int { @@ -160,8 +164,8 @@ fun Elements.firsTextNode(): String { return textNodes().firstOrNull()?.text().orEmpty() } -fun Elements.styleBackground(): String { - return attr("style").fetchStyleBackgroundUrl().optImageUrl() +fun Elements.styleBackground(default: Boolean = true): String { + return attr("style").fetchStyleBackgroundUrl().optImageUrl(default = default) } fun Element.parseStar(): Float { diff --git a/lib-common/src/main/java/com/xiaoyv/common/api/parser/entity/GroupDetailEntity.kt b/lib-common/src/main/java/com/xiaoyv/common/api/parser/entity/GroupDetailEntity.kt index 9db3de71..7e34eb52 100644 --- a/lib-common/src/main/java/com/xiaoyv/common/api/parser/entity/GroupDetailEntity.kt +++ b/lib-common/src/main/java/com/xiaoyv/common/api/parser/entity/GroupDetailEntity.kt @@ -16,6 +16,7 @@ import kotlinx.parcelize.Parcelize @Keep data class GroupDetailEntity( override var id: String = "", + var groupNumberId: String = "", var avatar: String = "", var name: String = "", var time: String = "", @@ -24,5 +25,5 @@ data class GroupDetailEntity( var recently: List = emptyList(), var otherGroups: List = emptyList(), var gh: String = "", - var isJoin: Boolean = false + var isJoin: Boolean = false, ) : IdEntity, Parcelable diff --git a/lib-common/src/main/java/com/xiaoyv/common/api/parser/entity/MediaChapterEntity.kt b/lib-common/src/main/java/com/xiaoyv/common/api/parser/entity/MediaChapterEntity.kt index 00035529..20185f21 100644 --- a/lib-common/src/main/java/com/xiaoyv/common/api/parser/entity/MediaChapterEntity.kt +++ b/lib-common/src/main/java/com/xiaoyv/common/api/parser/entity/MediaChapterEntity.kt @@ -19,5 +19,7 @@ data class MediaChapterEntity( var titleNative: String = "", var finished: Boolean = false, var time: String = "", - var commentCount: Int = 0 + var commentCount: Int = 0, + var stateText: String = "", + var aired: Boolean = false, ) : Parcelable, IdEntity diff --git a/lib-common/src/main/java/com/xiaoyv/common/api/parser/impl/GroupParser.kt b/lib-common/src/main/java/com/xiaoyv/common/api/parser/impl/GroupParser.kt index c81a663f..f1ae2b91 100644 --- a/lib-common/src/main/java/com/xiaoyv/common/api/parser/impl/GroupParser.kt +++ b/lib-common/src/main/java/com/xiaoyv/common/api/parser/impl/GroupParser.kt @@ -25,6 +25,8 @@ fun Document.parserGroupDetail(groupId: String): GroupDetailEntity { requireNoError() val entity = GroupDetailEntity(id = groupId) + entity.groupNumberId = select("form[name=new_comment]").attr("action") + .parseCount().toString() selectLegal("#columnA").apply { entity.avatar = select(".grp_box > img").attr("src").optImageUrl() diff --git a/lib-common/src/main/java/com/xiaoyv/common/api/parser/impl/MediaDetailParser.kt b/lib-common/src/main/java/com/xiaoyv/common/api/parser/impl/MediaDetailParser.kt index 61ffe9ab..c57580a0 100644 --- a/lib-common/src/main/java/com/xiaoyv/common/api/parser/impl/MediaDetailParser.kt +++ b/lib-common/src/main/java/com/xiaoyv/common/api/parser/impl/MediaDetailParser.kt @@ -35,6 +35,8 @@ fun Document.parserMediaChapters(): List { return select(".line_detail > ul > li").map { if (it.select("h6").isEmpty()) return@map null val entity = MediaChapterEntity() + entity.aired = it.select(".Air").isNotEmpty() + entity.stateText = it.select(".epAirStatus").attr("title") entity.id = it.select("h6 a").hrefId() entity.titleCn = it.select("h6 .tip").text().substringAfterLast("/").trim() entity.titleNative = it.select("h6 a").text() diff --git a/lib-common/src/main/java/com/xiaoyv/common/api/parser/impl/TimeParser.kt b/lib-common/src/main/java/com/xiaoyv/common/api/parser/impl/TimeParser.kt index 9e771f57..93deddf9 100644 --- a/lib-common/src/main/java/com/xiaoyv/common/api/parser/impl/TimeParser.kt +++ b/lib-common/src/main/java/com/xiaoyv/common/api/parser/impl/TimeParser.kt @@ -157,7 +157,7 @@ fun parserTimelineText(item: Element, entity: TimelineEntity) { entity.id = item.select("a.avatar").hrefId() entity.name = item.select(".info > a").firstOrNull()?.text().orEmpty() - entity.avatar = item.select("a.avatar > span").styleBackground() + entity.avatar = item.select("a.avatar > span").styleBackground(false) entity.title = item.select(".info, .info_full").fetchHtmlTitle() entity.content = item.select("p.status, .info_sub").text() }