feat: 等级显示 & 楼主提示

This commit is contained in:
HuanCheng65 2023-07-14 13:35:46 +08:00
parent 37e81673c7
commit a3ce72a971
No known key found for this signature in database
GPG Key ID: 5EC9DD60A32C7360
5 changed files with 156 additions and 14 deletions

View File

@ -33,9 +33,15 @@ fun Float.dpToPxFloat(): Float =
fun Float.spToPx(): Int = fun Float.spToPx(): Int =
(this * App.INSTANCE.resources.displayMetrics.scaledDensity + 0.5f).roundToInt() (this * App.INSTANCE.resources.displayMetrics.scaledDensity + 0.5f).roundToInt()
fun Float.spToPxFloat(): Float =
this * App.INSTANCE.resources.displayMetrics.scaledDensity + 0.5f
fun Float.pxToDp(): Int = fun Float.pxToDp(): Int =
(this / App.ScreenInfo.DENSITY + 0.5f).roundToInt() (this / App.ScreenInfo.DENSITY + 0.5f).roundToInt()
fun Float.pxToDpFloat(): Float =
this / App.ScreenInfo.DENSITY + 0.5f
fun Float.pxToSp(): Int = fun Float.pxToSp(): Int =
(this / App.INSTANCE.resources.displayMetrics.scaledDensity + 0.5f).roundToInt() (this / App.INSTANCE.resources.displayMetrics.scaledDensity + 0.5f).roundToInt()
@ -47,6 +53,13 @@ fun Int.pxToDp(): Int = this.toFloat().pxToDp()
fun Int.pxToSp(): Int = this.toFloat().pxToSp() fun Int.pxToSp(): Int = this.toFloat().pxToSp()
fun Float.pxToSpFloat(): Float = this / App.INSTANCE.resources.displayMetrics.scaledDensity + 0.5f
fun Int.pxToSpFloat(): Float = this.toFloat().pxToSpFloat()
fun Int.pxToDpFloat(): Float =
this.toFloat().pxToDpFloat()
inline fun <reified Data> String.fromJson(): Data { inline fun <reified Data> String.fromJson(): Data {
val type = object : TypeToken<Data>() {}.type val type = object : TypeToken<Data>() {}.type
return GsonUtil.getGson().fromJson(this, type) return GsonUtil.getGson().fromJson(this, type)

View File

@ -28,11 +28,11 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.ButtonDefaults import androidx.compose.material.ButtonDefaults
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.ModalBottomSheetLayout import androidx.compose.material.ModalBottomSheetLayout
import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.ModalBottomSheetValue
@ -77,6 +77,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
@ -90,6 +91,7 @@ import com.airbnb.lottie.compose.LottieConstants
import com.airbnb.lottie.compose.rememberLottieComposition import com.airbnb.lottie.compose.rememberLottieComposition
import com.huanchengfly.tieba.post.App import com.huanchengfly.tieba.post.App
import com.huanchengfly.tieba.post.R import com.huanchengfly.tieba.post.R
import com.huanchengfly.tieba.post.activities.UserActivity
import com.huanchengfly.tieba.post.api.TiebaApi import com.huanchengfly.tieba.post.api.TiebaApi
import com.huanchengfly.tieba.post.api.booleanToString import com.huanchengfly.tieba.post.api.booleanToString
import com.huanchengfly.tieba.post.api.models.protos.Post import com.huanchengfly.tieba.post.api.models.protos.Post
@ -129,6 +131,7 @@ import com.huanchengfly.tieba.post.ui.widgets.compose.TitleCentredToolbar
import com.huanchengfly.tieba.post.ui.widgets.compose.UserHeader import com.huanchengfly.tieba.post.ui.widgets.compose.UserHeader
import com.huanchengfly.tieba.post.ui.widgets.compose.VerticalDivider import com.huanchengfly.tieba.post.ui.widgets.compose.VerticalDivider
import com.huanchengfly.tieba.post.ui.widgets.compose.VerticalGrid import com.huanchengfly.tieba.post.ui.widgets.compose.VerticalGrid
import com.huanchengfly.tieba.post.ui.widgets.compose.buildChipInlineContent
import com.huanchengfly.tieba.post.ui.widgets.compose.rememberDialogState import com.huanchengfly.tieba.post.ui.widgets.compose.rememberDialogState
import com.huanchengfly.tieba.post.ui.widgets.compose.states.StateScreen import com.huanchengfly.tieba.post.ui.widgets.compose.states.StateScreen
import com.huanchengfly.tieba.post.utils.DateTimeUtils.getRelativeTimeString import com.huanchengfly.tieba.post.utils.DateTimeUtils.getRelativeTimeString
@ -137,6 +140,7 @@ import com.huanchengfly.tieba.post.utils.StringUtil
import com.huanchengfly.tieba.post.utils.StringUtil.getShortNumString import com.huanchengfly.tieba.post.utils.StringUtil.getShortNumString
import com.huanchengfly.tieba.post.utils.ThemeUtil import com.huanchengfly.tieba.post.utils.ThemeUtil
import com.huanchengfly.tieba.post.utils.TiebaUtil import com.huanchengfly.tieba.post.utils.TiebaUtil
import com.huanchengfly.tieba.post.utils.Util.getIconColorByLevel
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@ -145,6 +149,7 @@ import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlin.math.max
private fun getDescText( private fun getDescText(
time: Long?, time: Long?,
@ -944,7 +949,7 @@ fun ThreadPage(
} }
} }
item(key = "LoadPreviousBtn") { item(key = "LoadPreviousBtn") {
if (currentPageMin > 1) { if (hasPrevious) {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -952,7 +957,7 @@ fun ThreadPage(
viewModel.send( viewModel.send(
ThreadUiIntent.LoadPrevious( ThreadUiIntent.LoadPrevious(
threadId, threadId,
currentPageMax - 1, max(currentPageMax - 1, 1),
forumId, forumId,
postId = data postId = data
.first() .first()
@ -989,8 +994,9 @@ fun ThreadPage(
PostCard( PostCard(
postHolder = item, postHolder = item,
contentRenders = contentRenders[index], contentRenders = contentRenders[index],
immersiveMode = isImmersiveMode, threadAuthorId = author?.get { id } ?: 0L,
blocked = blocked, blocked = blocked,
immersiveMode = isImmersiveMode,
onAgree = { onAgree = {
val postHasAgreed = val postHasAgreed =
item.get { agree?.hasAgree == 1 } item.get { agree?.hasAgree == 1 }
@ -1201,6 +1207,7 @@ private fun BottomBar(
fun PostCard( fun PostCard(
postHolder: ImmutableHolder<Post>, postHolder: ImmutableHolder<Post>,
contentRenders: ImmutableList<PbContentRender>, contentRenders: ImmutableList<PbContentRender>,
threadAuthorId: Long = 0L,
blocked: Boolean = false, blocked: Boolean = false,
immersiveMode: Boolean = false, immersiveMode: Boolean = false,
onAgree: () -> Unit = {}, onAgree: () -> Unit = {},
@ -1279,17 +1286,21 @@ fun PostCard(
) )
}, },
name = { name = {
Text( UserNameText(
text = StringUtil.getUsernameAnnotatedString( userName = StringUtil.getUsernameAnnotatedString(
LocalContext.current, LocalContext.current,
author.name, author.name,
author.nameShow, author.nameShow
LocalContentColor.current ),
) userLevel = author.level_id,
isLz = author.id == threadAuthorId
) )
}, },
desc = { 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())
} }
) { ) {
if (post.floor > 1) { if (post.floor > 1) {
@ -1360,6 +1371,46 @@ fun PostCard(
) )
} }
@Composable
private fun UserNameText(
userName: AnnotatedString,
userLevel: Int,
modifier: Modifier = Modifier,
isLz: Boolean = false,
bawuType: String? = null,
) {
val text = buildAnnotatedString {
append(userName)
append(" ")
appendInlineContent("Level", alternateText = "$userLevel")
if (!bawuType.isNullOrBlank()) {
append(" ")
appendInlineContent("Bawu", alternateText = bawuType)
}
if (isLz) {
append(" ")
appendInlineContent("Lz")
}
}
Text(
text = text,
inlineContent = mapOf(
"Level" to buildChipInlineContent(
"18",
color = Color(getIconColorByLevel("$userLevel")),
backgroundColor = Color(getIconColorByLevel("$userLevel")).copy(alpha = 0.25f)
),
"Bawu" to buildChipInlineContent(
bawuType ?: "",
color = ExtendedTheme.colors.accent,
backgroundColor = ExtendedTheme.colors.accent.copy(alpha = 0.25f)
),
"Lz" to buildChipInlineContent(stringResource(id = R.string.tip_lz)),
),
modifier = modifier
)
}
@Composable @Composable
private fun ThreadMenu( private fun ThreadMenu(
isSeeLz: Boolean, isSeeLz: Boolean,

View File

@ -269,11 +269,13 @@ private fun StoreItem(
}, },
onClick = onUserClick, onClick = onUserClick,
) )
val title = buildAnnotatedString { val title = remember(info, hasUpdate) {
append(info.title) buildAnnotatedString {
if (hasUpdate) { append(info.title)
append(" ") if (hasUpdate) {
appendInlineContent("Update", info.postNo) append(" ")
appendInlineContent("Update", info.postNo)
}
} }
} }
val updateTip = stringResource( val updateTip = stringResource(

View File

@ -1,7 +1,16 @@
package com.huanchengfly.tieba.post.ui.widgets.compose package com.huanchengfly.tieba.post.ui.widgets.compose
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.InlineTextContent import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.LocalContentAlpha import androidx.compose.material.LocalContentAlpha
@ -14,9 +23,12 @@ import androidx.compose.material.icons.rounded.Link
import androidx.compose.material.icons.rounded.SlowMotionVideo import androidx.compose.material.icons.rounded.SlowMotionVideo
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.takeOrElse import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.ExperimentalTextApi import androidx.compose.ui.text.ExperimentalTextApi
@ -28,6 +40,7 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.text.style.LineHeightStyle
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
@ -35,8 +48,11 @@ import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.huanchengfly.tieba.post.R import com.huanchengfly.tieba.post.R
import com.huanchengfly.tieba.post.dpToPxFloat
import com.huanchengfly.tieba.post.pxToDp import com.huanchengfly.tieba.post.pxToDp
import com.huanchengfly.tieba.post.pxToSp import com.huanchengfly.tieba.post.pxToSp
import com.huanchengfly.tieba.post.pxToSpFloat
import com.huanchengfly.tieba.post.spToPxFloat
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.utils.EmoticonManager import com.huanchengfly.tieba.post.utils.EmoticonManager
import com.huanchengfly.tieba.post.utils.EmoticonUtil.emoticonString import com.huanchengfly.tieba.post.utils.EmoticonUtil.emoticonString
@ -320,4 +336,62 @@ fun TextWithMinWidth(
onTextLayout = onTextLayout, onTextLayout = onTextLayout,
style = style style = style
) )
}
@OptIn(ExperimentalTextApi::class)
@Composable
fun buildChipInlineContent(
text: String,
padding: PaddingValues = PaddingValues(vertical = 2.dp, horizontal = 4.dp),
textStyle: TextStyle = LocalTextStyle.current,
chipTextStyle: TextStyle = LocalTextStyle.current,
backgroundColor: Color = ExtendedTheme.colors.chip,
color: Color = ExtendedTheme.colors.onChip
): InlineTextContent {
val textMeasurer = rememberTextMeasurer()
val textSize = remember(text, textStyle) { textMeasurer.measure(text, textStyle).size }
val heightPx = textSize.height
val heightSp = heightPx.pxToSpFloat().sp
val textHeightPx = textStyle.fontSize.value.spToPxFloat() -
padding.calculateTopPadding().value.dpToPxFloat() -
padding.calculateBottomPadding().value.dpToPxFloat()
val fontSize = textHeightPx.pxToSpFloat().sp
val textWidthPx = textSize.width
val widthPx = textWidthPx +
padding.calculateStartPadding(LocalLayoutDirection.current).value.dpToPxFloat() +
padding.calculateEndPadding(LocalLayoutDirection.current).value.dpToPxFloat()
val widthSp = widthPx.pxToSpFloat().sp
return InlineTextContent(
placeholder = Placeholder(
width = widthSp,
height = heightSp,
placeholderVerticalAlign = PlaceholderVerticalAlign.TextCenter
),
children = {
Box(
modifier = Modifier
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
text = it.takeIf { it.isNotBlank() && it != "\uFFFD" } ?: text,
style = chipTextStyle.copy(
fontSize = fontSize,
lineHeight = fontSize,
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.Both
)
),
textAlign = TextAlign.Center,
color = color,
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(100))
.background(backgroundColor)
.padding(padding)
)
}
}
)
} }

View File

@ -15,6 +15,7 @@ message User {
int32 type = 7; int32 type = 7;
int32 userhide = 9; int32 userhide = 9;
int32 is_manager = 11; int32 is_manager = 11;
int32 level_id = 23;
int32 is_bawu = 25; int32 is_bawu = 25;
string bawu_type = 26; string bawu_type = 26;
string portraith = 27; string portraith = 27;
@ -34,5 +35,6 @@ message User {
int32 priv_thread = 92; int32 priv_thread = 92;
int32 isDefaultAvatar = 106; int32 isDefaultAvatar = 106;
uint32 total_agree_num = 118; uint32 total_agree_num = 118;
string level_name = 125;
string ip_address = 127; string ip_address = 127;
} }