feat: 自由复制

This commit is contained in:
HuanCheng65 2023-09-30 01:26:43 +08:00
parent eecc703c7f
commit 349b397278
No known key found for this signature in database
GPG Key ID: 5EC9DD60A32C7360
6 changed files with 273 additions and 52 deletions

View File

@ -0,0 +1,177 @@
package com.huanchengfly.tieba.post.ui.page.dialogs
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Close
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import com.huanchengfly.tieba.post.R
import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme
import com.huanchengfly.tieba.post.ui.widgets.compose.Button
import com.huanchengfly.tieba.post.ui.widgets.compose.TitleCentredToolbar
import com.huanchengfly.tieba.post.utils.TiebaUtil
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.spec.DestinationStyle
@Destination
@Composable
fun CopyTextPage(
text: String,
navigator: DestinationsNavigator,
) {
val context = LocalContext.current
CopyTextPageContent(
text = text,
onCopy = {
TiebaUtil.copyText(context, it)
},
onCancel = {
navigator.navigateUp()
}
)
}
object CopyTextDialogStyle : DestinationStyle.Dialog {
override val properties: DialogProperties
get() = DialogProperties(
usePlatformDefaultWidth = false,
)
}
@Destination(
style = CopyTextDialogStyle::class
)
@Composable
fun CopyTextDialogPage(
text: String,
navigator: DestinationsNavigator,
) {
val context = LocalContext.current
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.fillMaxSize()
.background(color = ExtendedTheme.colors.windowBackground)
) {
CopyTextPageContent(
text = text,
onCopy = {
TiebaUtil.copyText(context, it)
},
onCancel = {
navigator.navigateUp()
}
)
}
}
@Composable
private fun CopyTextPageContent(
text: String,
onCopy: (String) -> Unit,
onCancel: () -> Unit,
) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(color = ExtendedTheme.colors.windowBackground)
.systemBarsPadding()
.padding(bottom = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
TitleCentredToolbar(
title = {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResource(id = R.string.title_copy),
style = MaterialTheme.typography.h6,
fontWeight = FontWeight.Bold
)
Text(
text = stringResource(id = R.string.tip_copy_text),
style = MaterialTheme.typography.caption
)
}
},
navigationIcon = {
IconButton(onClick = onCancel) {
Icon(
imageVector = Icons.Rounded.Close,
contentDescription = stringResource(id = R.string.btn_close)
)
}
}
)
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState())
.weight(1f),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
SelectionContainer {
Text(
text = text,
modifier = Modifier.fillMaxWidth(),
style = MaterialTheme.typography.body1
)
}
}
Button(
modifier = Modifier.fillMaxWidth(),
onClick = {
onCopy(text)
onCancel()
}
) {
Text(text = stringResource(id = R.string.btn_copy_all))
}
Button(
modifier = Modifier.fillMaxWidth(),
onClick = {
onCancel()
},
colors = ButtonDefaults.buttonColors(
backgroundColor = ExtendedTheme.colors.text.copy(alpha = 0.1f),
contentColor = ExtendedTheme.colors.text
)
) {
Text(text = stringResource(id = R.string.btn_close))
}
}
}
}

View File

@ -49,6 +49,7 @@ import com.huanchengfly.tieba.post.arch.wrapImmutable
import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme
import com.huanchengfly.tieba.post.ui.page.LocalNavigator import com.huanchengfly.tieba.post.ui.page.LocalNavigator
import com.huanchengfly.tieba.post.ui.page.ProvideNavigator import com.huanchengfly.tieba.post.ui.page.ProvideNavigator
import com.huanchengfly.tieba.post.ui.page.destinations.CopyTextDialogPageDestination
import com.huanchengfly.tieba.post.ui.page.destinations.ReplyPageDestination import com.huanchengfly.tieba.post.ui.page.destinations.ReplyPageDestination
import com.huanchengfly.tieba.post.ui.page.destinations.ThreadPageDestination import com.huanchengfly.tieba.post.ui.page.destinations.ThreadPageDestination
import com.huanchengfly.tieba.post.ui.page.thread.PostAgreeBtn import com.huanchengfly.tieba.post.ui.page.thread.PostAgreeBtn
@ -142,6 +143,7 @@ internal fun SubPostsContent(
loadFromSubPost: Boolean = false, loadFromSubPost: Boolean = false,
isSheet: Boolean = false isSheet: Boolean = false
) { ) {
val context = LocalContext.current
val navigator = LocalNavigator.current val navigator = LocalNavigator.current
val account = LocalAccount.current val account = LocalAccount.current
@ -406,6 +408,11 @@ internal fun SubPostsContent(
) )
) )
}, },
onMenuCopyClick = {
navigator.navigate(
CopyTextDialogPageDestination(it)
)
},
) { ) {
deleteSubPost = null deleteSubPost = null
confirmDeleteDialogState.show() confirmDeleteDialogState.show()
@ -465,6 +472,12 @@ internal fun SubPostsContent(
) )
) )
}, },
onMenuCopyClick = {
navigator.navigate(
CopyTextDialogPageDestination(it)
)
// TiebaUtil.copyText(context, it)
},
onMenuDeleteClick = { onMenuDeleteClick = {
deleteSubPost = it.wrapImmutable() deleteSubPost = it.wrapImmutable()
confirmDeleteDialogState.show() confirmDeleteDialogState.show()
@ -499,6 +512,7 @@ private fun SubPostItem(
canDelete: (SubPostList) -> Boolean = { false }, canDelete: (SubPostList) -> Boolean = { false },
onAgree: (SubPostList) -> Unit = {}, onAgree: (SubPostList) -> Unit = {},
onReplyClick: (SubPostList) -> Unit = {}, onReplyClick: (SubPostList) -> Unit = {},
onMenuCopyClick: ((String) -> Unit)? = null,
onMenuDeleteClick: ((SubPostList) -> Unit)? = null, onMenuDeleteClick: ((SubPostList) -> Unit)? = null,
) { ) {
val (subPost, contentRenders, blocked) = item val (subPost, contentRenders, blocked) = item
@ -532,15 +546,15 @@ private fun SubPostItem(
Text(text = stringResource(id = R.string.btn_reply)) Text(text = stringResource(id = R.string.btn_reply))
} }
} }
DropdownMenuItem( if (onMenuCopyClick != null) {
onClick = { DropdownMenuItem(
TiebaUtil.copyText( onClick = {
context, onMenuCopyClick(contentRenders.joinToString("\n") { it.toString() })
contentRenders.joinToString("\n") { it.toString() }) menuState.expanded = false
menuState.expanded = false }
) {
Text(text = stringResource(id = R.string.menu_copy))
} }
) {
Text(text = stringResource(id = R.string.menu_copy))
} }
DropdownMenuItem( DropdownMenuItem(
onClick = { onClick = {

View File

@ -102,7 +102,6 @@ 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.User
import com.huanchengfly.tieba.post.api.models.protos.bawuType import com.huanchengfly.tieba.post.api.models.protos.bawuType
import com.huanchengfly.tieba.post.api.models.protos.plainText import com.huanchengfly.tieba.post.api.models.protos.plainText
import com.huanchengfly.tieba.post.api.models.protos.renders
import com.huanchengfly.tieba.post.api.retrofit.exception.getErrorMessage import com.huanchengfly.tieba.post.api.retrofit.exception.getErrorMessage
import com.huanchengfly.tieba.post.arch.GlobalEvent import com.huanchengfly.tieba.post.arch.GlobalEvent
import com.huanchengfly.tieba.post.arch.ImmutableHolder import com.huanchengfly.tieba.post.arch.ImmutableHolder
@ -123,6 +122,7 @@ import com.huanchengfly.tieba.post.ui.common.theme.compose.invertChipContent
import com.huanchengfly.tieba.post.ui.common.theme.compose.pullRefreshIndicator import com.huanchengfly.tieba.post.ui.common.theme.compose.pullRefreshIndicator
import com.huanchengfly.tieba.post.ui.common.theme.compose.threadBottomBar import com.huanchengfly.tieba.post.ui.common.theme.compose.threadBottomBar
import com.huanchengfly.tieba.post.ui.page.ProvideNavigator import com.huanchengfly.tieba.post.ui.page.ProvideNavigator
import com.huanchengfly.tieba.post.ui.page.destinations.CopyTextPageDestination
import com.huanchengfly.tieba.post.ui.page.destinations.ForumPageDestination import com.huanchengfly.tieba.post.ui.page.destinations.ForumPageDestination
import com.huanchengfly.tieba.post.ui.page.destinations.ReplyPageDestination import com.huanchengfly.tieba.post.ui.page.destinations.ReplyPageDestination
import com.huanchengfly.tieba.post.ui.page.destinations.SubPostsSheetPageDestination import com.huanchengfly.tieba.post.ui.page.destinations.SubPostsSheetPageDestination
@ -873,6 +873,11 @@ fun ThreadPage(
) )
} }
}, },
onMenuCopyClick = {
navigator.navigate(
CopyTextPageDestination(it)
)
},
onMenuFavoriteClick = { onMenuFavoriteClick = {
val isPostCollected = val isPostCollected =
it.id == thread?.get { collectMarkPid.toLongOrNull() } it.id == thread?.get { collectMarkPid.toLongOrNull() }
@ -1209,6 +1214,11 @@ fun ThreadPage(
) )
) )
}, },
onMenuCopyClick = {
navigator.navigate(
CopyTextPageDestination(it)
)
},
onMenuFavoriteClick = { onMenuFavoriteClick = {
viewModel.send( viewModel.send(
ThreadUiIntent.AddFavorite( ThreadUiIntent.AddFavorite(
@ -1540,6 +1550,7 @@ fun PostCard(
onReplyClick: (Post) -> Unit = {}, onReplyClick: (Post) -> Unit = {},
onSubPostReplyClick: ((Post, SubPostList) -> Unit)? = null, onSubPostReplyClick: ((Post, SubPostList) -> Unit)? = null,
onOpenSubPosts: (subPostId: Long) -> Unit = {}, onOpenSubPosts: (subPostId: Long) -> Unit = {},
onMenuCopyClick: ((String) -> Unit)? = null,
onMenuFavoriteClick: ((Post) -> Unit)? = null, onMenuFavoriteClick: ((Post) -> Unit)? = null,
onMenuDeleteClick: ((Post) -> Unit)? = null, onMenuDeleteClick: ((Post) -> Unit)? = null,
) { ) {
@ -1591,13 +1602,15 @@ fun PostCard(
Text(text = stringResource(id = R.string.btn_reply)) Text(text = stringResource(id = R.string.btn_reply))
} }
} }
DropdownMenuItem( if (onMenuCopyClick != null) {
onClick = { DropdownMenuItem(
TiebaUtil.copyText(context, post.content.plainText) onClick = {
menuState.expanded = false onMenuCopyClick(post.content.plainText)
menuState.expanded = false
}
) {
Text(text = stringResource(id = R.string.menu_copy))
} }
) {
Text(text = stringResource(id = R.string.menu_copy))
} }
DropdownMenuItem( DropdownMenuItem(
onClick = { onClick = {
@ -1744,6 +1757,9 @@ fun PostCard(
onSubPostReplyClick?.invoke(post, it) onSubPostReplyClick?.invoke(post, it)
}, },
onOpenSubPosts = onOpenSubPosts, onOpenSubPosts = onOpenSubPosts,
onMenuCopyClick = {
onMenuCopyClick?.invoke(it.content.plainText)
}
) )
} }
} }
@ -1781,6 +1797,7 @@ private fun SubPostItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onReplyClick: ((SubPostList) -> Unit)?, onReplyClick: ((SubPostList) -> Unit)?,
onOpenSubPosts: (Long) -> Unit, onOpenSubPosts: (Long) -> Unit,
onMenuCopyClick: ((SubPostList) -> Unit)?,
) { ) {
val context = LocalContext.current val context = LocalContext.current
val menuState = rememberMenuState() val menuState = rememberMenuState()
@ -1797,15 +1814,15 @@ private fun SubPostItem(
Text(text = stringResource(id = R.string.title_reply)) Text(text = stringResource(id = R.string.title_reply))
} }
} }
DropdownMenuItem( if (onMenuCopyClick != null) {
onClick = { DropdownMenuItem(
TiebaUtil.copyText( onClick = {
context, onMenuCopyClick(subPostList.get())
subPostList.get { content.renders.joinToString(" ") { it.toString() } }) menuState.expanded = false
menuState.expanded = false }
) {
Text(text = stringResource(id = R.string.menu_copy))
} }
) {
Text(text = stringResource(id = R.string.menu_copy))
} }
DropdownMenuItem( DropdownMenuItem(
onClick = { onClick = {

View File

@ -1,6 +1,8 @@
package com.huanchengfly.tieba.post.ui.widgets.compose package com.huanchengfly.tieba.post.ui.widgets.compose
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
@ -47,23 +49,26 @@ fun Button(
) { ) {
val contentColor by colors.contentColor(enabled) val contentColor by colors.contentColor(enabled)
Surface( Surface(
onClick = onClick, modifier = Modifier
modifier = modifier, .clickable(
enabled = enabled, onClick = onClick,
enabled = enabled,
interactionSource = interactionSource,
indication = LocalIndication.current
)
.then(modifier),
shape = shape, shape = shape,
color = colors.backgroundColor(enabled).value, color = colors.backgroundColor(enabled).value,
contentColor = contentColor.copy(alpha = 1f), contentColor = contentColor.copy(alpha = 1f),
border = border, border = border,
elevation = elevation?.elevation(enabled, interactionSource)?.value ?: 0.dp, elevation = elevation?.elevation(enabled, interactionSource)?.value ?: 0.dp,
interactionSource = interactionSource,
) { ) {
CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) { CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
ProvideTextStyle( ProvideTextStyle(
value = MaterialTheme.typography.button value = MaterialTheme.typography.button
) { ) {
Row( Row(
Modifier Modifier.padding(contentPadding),
.padding(contentPadding),
horizontalArrangement = Arrangement.Center, horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
content = content content = content

View File

@ -27,6 +27,7 @@ import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.IconButton import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.ProvideTextStyle
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.TopAppBar import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@ -209,7 +210,7 @@ fun TitleCentredToolbar(
) { ) {
TitleCentredToolbar( TitleCentredToolbar(
title = { title = {
Text(text = title, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.h6) Text(text = title)
}, },
modifier = modifier, modifier = modifier,
insets = insets, insets = insets,
@ -241,11 +242,13 @@ fun TitleCentredToolbar(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxHeight() modifier = Modifier.fillMaxHeight()
) { ) {
navigationIcon?.invoke() ProvideContentColor(color = ExtendedTheme.colors.onTopBar) {
navigationIcon?.invoke()
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
actions() actions()
}
} }
Row( Row(
@ -255,8 +258,10 @@ fun TitleCentredToolbar(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center horizontalArrangement = Arrangement.Center
) { ) {
ProvideContentColor(color = ExtendedTheme.colors.onTopBar) { ProvideTextStyle(value = MaterialTheme.typography.h6.copy(fontWeight = FontWeight.Bold)) {
title() ProvideContentColor(color = ExtendedTheme.colors.onTopBar) {
title()
}
} }
} }
} }
@ -275,21 +280,12 @@ fun Toolbar(
actions: @Composable RowScope.() -> Unit = {}, actions: @Composable RowScope.() -> Unit = {},
content: (@Composable ColumnScope.() -> Unit)? = null, content: (@Composable ColumnScope.() -> Unit)? = null,
) { ) {
TopAppBarContainer( Toolbar(
topBar = { title = {
TopAppBar( Text(text = title)
title = {
ProvideContentColor(color = ExtendedTheme.colors.onTopBar) {
Text(text = title, fontWeight = FontWeight.Bold)
}
},
actions = actions,
navigationIcon = navigationIcon,
backgroundColor = ExtendedTheme.colors.topBar,
contentColor = ExtendedTheme.colors.onTopBar,
elevation = 0.dp
)
}, },
navigationIcon = navigationIcon,
actions = actions,
content = content content = content
) )
} }
@ -305,10 +301,20 @@ fun Toolbar(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { title = {
ProvideContentColor(color = ExtendedTheme.colors.onTopBar, content = title) ProvideTextStyle(value = MaterialTheme.typography.h6.copy(fontWeight = FontWeight.Bold)) {
ProvideContentColor(color = ExtendedTheme.colors.onTopBar, content = title)
}
},
actions = {
ProvideContentColor(color = ExtendedTheme.colors.onTopBar) {
actions()
}
},
navigationIcon = {
ProvideContentColor(color = ExtendedTheme.colors.onTopBar) {
navigationIcon?.invoke()
}
}, },
actions = actions,
navigationIcon = navigationIcon,
backgroundColor = ExtendedTheme.colors.topBar, backgroundColor = ExtendedTheme.colors.topBar,
contentColor = ExtendedTheme.colors.onTopBar, contentColor = ExtendedTheme.colors.onTopBar,
elevation = 0.dp elevation = 0.dp

View File

@ -729,4 +729,6 @@
<string name="title_hide_reply">隐藏回贴入口</string> <string name="title_hide_reply">隐藏回贴入口</string>
<string name="title_settings_use_themed_icon">应用图标使用动态取色</string> <string name="title_settings_use_themed_icon">应用图标使用动态取色</string>
<string name="tip_settings_use_themed_icon_summary_not_supported">当前正在使用的应用图标暂不支持动态取色</string> <string name="tip_settings_use_themed_icon_summary_not_supported">当前正在使用的应用图标暂不支持动态取色</string>
<string name="btn_copy_all">复制全部</string>
<string name="title_copy">复制</string>
</resources> </resources>