feat: 楼层长按菜单

This commit is contained in:
HuanCheng65 2023-07-21 18:57:07 +08:00
parent d5f0b1c61a
commit fad05912ae
No known key found for this signature in database
GPG Key ID: 5EC9DD60A32C7360
9 changed files with 202 additions and 115 deletions

View File

@ -159,16 +159,9 @@ private val PbContent.picUrl: String
cdnSrcActive,
src
)
val List<PbContent>.plainText: String
get() = joinToString(separator = "") {
when (it.type) {
0, 1, 4, 9, 27 -> it.text
2 -> "#(${it.c})"
3, 20 -> "[图片]"
5 -> "[视频]"
else -> ""
}
}
get() = renders.joinToString("\n") { toString() }
@OptIn(ExperimentalTextApi::class)
val List<PbContent>.renders: ImmutableList<PbContentRender>
@ -307,6 +300,7 @@ val List<PbContent>.renders: ImmutableList<PbContentRender>
return renders.toImmutableList()
}
val Post.contentRenders: ImmutableList<PbContentRender>
get() {
val renders = content.renders
@ -325,6 +319,7 @@ val Post.contentRenders: ImmutableList<PbContentRender>
} else it
}.toImmutableList()
}
val User.bawuType: String?
get() = if (is_bawu == 1) {
if (bawu_type == "manager") "吧主" else "小吧主"

View File

@ -64,6 +64,10 @@ data class TextContentRender(
) : PbContentRender {
constructor(text: String) : this(AnnotatedString(text))
override fun toString(): String {
return text.toString()
}
@Composable
override fun Render() {
PbContentText(text = text, fontSize = 15.sp, style = MaterialTheme.typography.body1)
@ -134,6 +138,10 @@ data class PicContentRender(
contentScale = ContentScale.Crop
)
}
override fun toString(): String {
return "[图片]"
}
}
@Stable
@ -148,6 +156,10 @@ data class VoiceContentRender(
}
VoicePlayer(url = voiceUrl, duration = duration)
}
override fun toString(): String {
return "[视频]"
}
}
@Stable
@ -189,6 +201,10 @@ data class VideoContentRender(
}
}
}
override fun toString(): String {
return "[语音]"
}
}
@Composable

View File

@ -217,7 +217,6 @@ private fun HistoryItem(
) {
val menuState = rememberMenuState()
LongClickMenu(
menuState = menuState,
menuContent = {
DropdownMenuItem(onClick = {
onDelete(info)
@ -226,6 +225,7 @@ private fun HistoryItem(
Text(text = stringResource(id = R.string.title_delete))
}
},
menuState = menuState,
onClick = { onClick(info) }
) {
Column(

View File

@ -268,7 +268,6 @@ private fun ForumItem(
}
}
LongClickMenu(
menuState = menuState,
menuContent = {
if (isTopForum) {
DropdownMenuItem(
@ -306,6 +305,7 @@ private fun ForumItem(
Text(text = stringResource(id = R.string.button_unfollow))
}
},
menuState = menuState,
onClick = {
navigator.navigate(ForumPageDestination(item.forumName))
}

View File

@ -298,7 +298,7 @@ internal fun SubPostsContent(
)
)
},
onClickContent = {
onReplyClick = {
navigator.navigate(
ReplyPageDestination(
forumId = forumId,

View File

@ -28,8 +28,8 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
@ -96,6 +96,7 @@ 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.plainText
import com.huanchengfly.tieba.post.api.retrofit.exception.getErrorMessage
import com.huanchengfly.tieba.post.arch.ImmutableHolder
import com.huanchengfly.tieba.post.arch.collectPartialAsState
@ -127,6 +128,7 @@ 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.ListMenuItem
import com.huanchengfly.tieba.post.ui.widgets.compose.LoadMoreLayout
import com.huanchengfly.tieba.post.ui.widgets.compose.LongClickMenu
import com.huanchengfly.tieba.post.ui.widgets.compose.MyBackHandler
import com.huanchengfly.tieba.post.ui.widgets.compose.MyScaffold
import com.huanchengfly.tieba.post.ui.widgets.compose.PromptDialog
@ -1041,7 +1043,9 @@ fun ThreadPage(
subPostContents = subPostContents[index],
threadAuthorId = author?.get { id } ?: 0L,
blocked = blocked,
canDelete = { it.author_id == user.get { id } },
immersiveMode = isImmersiveMode,
isCollected = { it.id == thread?.get { collectMarkPid.toLongOrNull() } },
onAgree = {
val postHasAgreed =
item.get { agree?.hasAgree == 1 }
@ -1053,7 +1057,7 @@ fun ThreadPage(
)
)
},
onClickContent = {
onReplyClick = {
navigator.navigate(
ReplyPageDestination(
forumId = curForumId ?: 0,
@ -1080,6 +1084,37 @@ fun ThreadPage(
)
}
},
onMenuCopyClick = {
TiebaUtil.copyText(context, it.content.plainText)
},
onMenuReportClick = {
TiebaUtil.reportPost(context, it.id.toString())
},
onMenuFavoriteClick = {
val isPostCollected =
it.id == thread?.get { collectMarkPid.toLongOrNull() }
val fid = forum?.get { id } ?: forumId
val tbs = anti?.get { tbs }
if (fid != null) {
if (isPostCollected) {
viewModel.send(
ThreadUiIntent.RemoveFavorite(
threadId = threadId,
forumId = fid,
tbs = tbs
)
)
} else {
viewModel.send(
ThreadUiIntent.AddFavorite(
threadId = threadId,
postId = it.id,
floor = it.floor
)
)
}
}
}
)
}
if (data.isEmpty()) {
@ -1298,11 +1333,17 @@ fun PostCard(
subPostContents: ImmutableList<AnnotatedString> = persistentListOf(),
threadAuthorId: Long = 0L,
blocked: Boolean = false,
canDelete: (Post) -> Boolean = { false },
immersiveMode: Boolean = false,
isCollected: (Post) -> Boolean = { false },
showSubPosts: Boolean = true,
onAgree: () -> Unit = {},
onClickContent: (Post) -> Unit = {},
onReplyClick: (Post) -> Unit = {},
onOpenSubPosts: (subPostId: Long) -> Unit = {},
onMenuCopyClick: ((Post) -> Unit)? = null,
onMenuFavoriteClick: ((Post) -> Unit)? = null,
onMenuReportClick: ((Post) -> Unit)? = null,
onMenuDeleteClick: ((Post) -> Unit)? = null,
) {
val context = LocalContext.current
if (blocked && !immersiveMode) {
@ -1325,7 +1366,7 @@ fun PostCard(
}
return
}
val (post) = postHolder
val post = remember(postHolder) { postHolder.get() }
val hasPadding = remember(key1 = postHolder, key2 = immersiveMode) {
postHolder.get { floor > 1 } && !immersiveMode
}
@ -1343,6 +1384,41 @@ fun PostCard(
val subPosts = remember(postHolder) {
post.sub_post_list?.sub_post_list?.toImmutableList() ?: persistentListOf()
}
LongClickMenu(
indication = null,
onClick = {
onReplyClick(post)
},
menuContent = {
DropdownMenuItem(onClick = { onReplyClick(post) }) {
Text(text = stringResource(id = R.string.btn_reply))
}
if (onMenuCopyClick != null) {
DropdownMenuItem(onClick = { onMenuCopyClick(post) }) {
Text(text = stringResource(id = R.string.menu_copy))
}
}
if (onMenuFavoriteClick != null) {
DropdownMenuItem(onClick = { onMenuFavoriteClick(post) }) {
if (isCollected(post)) {
Text(text = stringResource(id = R.string.title_collect))
} else {
Text(text = stringResource(id = R.string.title_collect_on))
}
}
}
if (onMenuReportClick != null) {
DropdownMenuItem(onClick = { onMenuReportClick(post) }) {
Text(text = stringResource(id = R.string.title_report))
}
}
if (canDelete(post) && onMenuDeleteClick != null) {
DropdownMenuItem(onClick = { onMenuDeleteClick(post) }) {
Text(text = stringResource(id = R.string.title_delete))
}
}
}
) {
Card(
header = {
if (!immersiveMode) {
@ -1367,7 +1443,13 @@ fun PostCard(
)
},
desc = {
Text(text = getDescText(post.time.toLong(), post.floor, author.ip_address))
Text(
text = getDescText(
post.time.toLong(),
post.floor,
author.ip_address
)
)
},
onClick = {
UserActivity.launch(context, author.id.toString())
@ -1384,17 +1466,10 @@ fun PostCard(
}
},
content = {
SelectionContainer {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = paddingModifier
.fillMaxWidth()
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null
) {
onClickContent(post)
}
) {
if (showTitle) {
Text(
@ -1406,7 +1481,6 @@ fun PostCard(
contentRenders.forEach { it.Render() }
}
}
if (showSubPosts && post.sub_post_number > 0 && subPostContents.isNotEmpty() && !immersiveMode) {
Column(
@ -1458,6 +1532,7 @@ fun PostCard(
}
}
)
}
}
@Composable

View File

@ -2,6 +2,7 @@ package com.huanchengfly.tieba.post.ui.widgets.compose
import android.util.Log
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Indication
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
@ -24,7 +25,6 @@ import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
@ -106,19 +106,19 @@ fun ClickMenu(
}
}
@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LongClickMenu(
menuContent: @Composable() (ColumnScope.() -> Unit),
modifier: Modifier = Modifier,
menuState: MenuState = rememberMenuState(),
menuContent: @Composable ColumnScope.() -> Unit,
onClick: (() -> Unit)? = null,
shape: Shape = RoundedCornerShape(14.dp),
modifier: Modifier = Modifier,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
indication: Indication? = LocalIndication.current,
content: @Composable () -> Unit
) {
val coroutineScope = rememberCoroutineScope()
val interactionSource = remember { MutableInteractionSource() }
val indication = LocalIndication.current
LaunchedEffect(key1 = null) {
coroutineScope.launch {
interactionSource.interactions

View File

@ -78,7 +78,6 @@ fun AccountNavIcon(
val context = LocalContext.current
val menuState = rememberMenuState()
LongClickMenu(
menuState = menuState,
menuContent = {
val allAccounts = AccountUtil.allAccounts
allAccounts.forEach {
@ -129,6 +128,7 @@ fun AccountNavIcon(
Text(text = stringResource(id = R.string.title_new_account))
}
},
menuState = menuState,
onClick = onClick,
shape = CircleShape
) {

View File

@ -696,4 +696,5 @@
<string name="title_image_watermark_user_name">显示用户名</string>
<string name="title_image_watermark_forum_name">显示吧名</string>
<string name="title_modify_username">修改用户名</string>
<string name="btn_reply">回复</string>
</resources>