Merge pull request #68 from Kevinlinpr/nagisa

修复动态-短视频界面的各种bug并优化ui
This commit is contained in:
2025-11-12 10:32:54 +08:00
committed by GitHub
18 changed files with 215 additions and 63 deletions

View File

@@ -53,7 +53,8 @@
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/Theme.App.Starting" android:theme="@style/Theme.App.Starting"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize"
android:configChanges="fontScale|orientation|screenSize|keyboardHidden">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />

View File

@@ -7,6 +7,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Configuration
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@@ -22,6 +23,8 @@ import androidx.compose.animation.SharedTransitionScope
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner
@@ -57,6 +60,21 @@ class MainActivity : ComponentActivity() {
private val scope = CoroutineScope(Dispatchers.Main) private val scope = CoroutineScope(Dispatchers.Main)
val context = this val context = this
override fun attachBaseContext(newBase: Context) {
// 禁用字体缩放,固定字体大小为系统默认大小
val configuration = Configuration(newBase.resources.configuration)
configuration.fontScale = 1.0f
val context = newBase.createConfigurationContext(configuration)
super.attachBaseContext(context)
}
override fun onConfigurationChanged(newConfig: Configuration) {
// 确保配置变化时字体缩放保持为 1.0
val config = Configuration(newConfig)
config.fontScale = 1.0f
super.onConfigurationChanged(config)
}
// 请求通知权限 // 请求通知权限
private val requestPermissionLauncher = registerForActivityResult( private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission(), ActivityResultContracts.RequestPermission(),
@@ -128,6 +146,15 @@ class MainActivity : ComponentActivity() {
} }
setContent { setContent {
// 强制字体缩放为 1.0 - 通过覆盖 Density 来实现
val density = LocalDensity.current
val fixedDensity = remember {
androidx.compose.ui.unit.Density(
density = density.density,
fontScale = 1.0f
)
}
var showSplash by remember { mutableStateOf(true) } var showSplash by remember { mutableStateOf(true) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
@@ -139,7 +166,8 @@ class MainActivity : ComponentActivity() {
SplashScreen() SplashScreen()
} else { } else {
CompositionLocalProvider( CompositionLocalProvider(
LocalAppTheme provides AppState.appTheme LocalAppTheme provides AppState.appTheme,
LocalDensity provides fixedDensity
) { ) {
CheckUpdateDialog() CheckUpdateDialog()
// 全局挂载积分底部弹窗 Host // 全局挂载积分底部弹窗 Host

View File

@@ -2,6 +2,7 @@ package com.aiosman.ravenow
import android.app.Application import android.app.Application
import android.content.Context import android.content.Context
import android.content.res.Configuration
import android.util.Log import android.util.Log
import com.google.firebase.FirebaseApp import com.google.firebase.FirebaseApp
import com.google.firebase.perf.FirebasePerformance import com.google.firebase.perf.FirebasePerformance
@@ -11,6 +12,14 @@ import com.google.firebase.perf.FirebasePerformance
*/ */
class RaveNowApplication : Application() { class RaveNowApplication : Application() {
override fun attachBaseContext(base: Context) {
// 禁用字体缩放,固定字体大小为系统默认大小
val configuration = Configuration(base.resources.configuration)
configuration.fontScale = 1.0f
val context = base.createConfigurationContext(configuration)
super.attachBaseContext(context)
}
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()

View File

@@ -40,6 +40,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.R import com.aiosman.ravenow.R
import com.aiosman.ravenow.entity.CommentEntity import com.aiosman.ravenow.entity.CommentEntity
import com.aiosman.ravenow.ui.composables.EditCommentBottomModal import com.aiosman.ravenow.ui.composables.EditCommentBottomModal
@@ -88,6 +89,7 @@ fun CommentModalContent(
} }
) )
val commentViewModel = model.commentsViewModel val commentViewModel = model.commentsViewModel
val AppColors = LocalAppTheme.current
var navBarHeight = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() var navBarHeight = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
@@ -99,10 +101,24 @@ fun CommentModalContent(
var bottomPadding by remember { mutableStateOf(0.dp) } var bottomPadding by remember { mutableStateOf(0.dp) }
var softwareKeyboardController = LocalSoftwareKeyboardController.current var softwareKeyboardController = LocalSoftwareKeyboardController.current
var replyComment by remember { mutableStateOf<CommentEntity?>(null) } var replyComment by remember { mutableStateOf<CommentEntity?>(null) }
var shouldAutoFocus by remember { mutableStateOf(false) }
LaunchedEffect(imePadding) { LaunchedEffect(imePadding) {
bottomPadding = imePadding.dp bottomPadding = imePadding.dp
} }
// 当设置回复评论时,自动聚焦到输入框
LaunchedEffect(replyComment) {
if (replyComment != null) {
// 延迟一下,确保输入框已经渲染
kotlinx.coroutines.delay(100)
shouldAutoFocus = true
// 请求显示键盘
softwareKeyboardController?.show()
} else {
shouldAutoFocus = false
}
}
DisposableEffect(Unit) { DisposableEffect(Unit) {
onDispose { onDispose {
onDismiss() onDismiss()
@@ -113,7 +129,7 @@ fun CommentModalContent(
onDismissRequest = { onDismissRequest = {
showCommentMenu = false showCommentMenu = false
}, },
containerColor = Color.White, containerColor = AppColors.background,
sheetState = rememberModalBottomSheetState( sheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true skipPartiallyExpanded = true
), ),
@@ -152,12 +168,13 @@ fun CommentModalContent(
stringResource(R.string.comment), stringResource(R.string.comment),
fontSize = 18.sp, fontSize = 18.sp,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
color = AppColors.text,
modifier = Modifier.align(Alignment.Center) modifier = Modifier.align(Alignment.Center)
) )
} }
HorizontalDivider( HorizontalDivider(
color = Color(0xFFF7F7F7) color = AppColors.divider
) )
Row( Row(
modifier = Modifier modifier = Modifier
@@ -169,7 +186,7 @@ fun CommentModalContent(
Text( Text(
text = stringResource(id = R.string.comment_count, commentCount), text = stringResource(id = R.string.comment_count, commentCount),
fontSize = 14.sp, fontSize = 14.sp,
color = Color(0xff666666) color = AppColors.secondaryText
) )
OrderSelectionComponent { OrderSelectionComponent {
commentViewModel.order = it commentViewModel.order = it
@@ -193,7 +210,9 @@ fun CommentModalContent(
}, },
onReply = { parentComment, _, _, _ -> onReply = { parentComment, _, _, _ ->
// 设置回复的评论,这样 EditCommentBottomModal 会显示回复输入框
// CommentContent 内部已经处理了游客模式检查,所以这里直接设置即可
replyComment = parentComment
}, },
) )
Spacer(modifier = Modifier.height(72.dp)) Spacer(modifier = Modifier.height(72.dp))
@@ -204,9 +223,12 @@ fun CommentModalContent(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.background(Color(0xfff7f7f7)) .background(AppColors.secondaryBackground)
) { ) {
EditCommentBottomModal(replyComment) { EditCommentBottomModal(
replyComment = replyComment,
autoFocus = shouldAutoFocus
) {
commentViewModel.viewModelScope.launch { commentViewModel.viewModelScope.launch {
if (replyComment != null) { if (replyComment != null) {
if (replyComment?.parentCommentId != null) { if (replyComment?.parentCommentId != null) {
@@ -224,6 +246,13 @@ fun CommentModalContent(
// 顶级评论 // 顶级评论
commentViewModel.createComment(it) commentViewModel.createComment(it)
} }
// 评论创建成功后调用回调
onCommentAdded()
// 清空回复状态和自动聚焦状态
replyComment = null
shouldAutoFocus = false
// 隐藏键盘
softwareKeyboardController?.hide()
} }
} }

View File

@@ -36,6 +36,7 @@ import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
@@ -60,10 +61,15 @@ fun EditCommentBottomModal(
var navBarHeight = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() var navBarHeight = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
val context = LocalContext.current val context = LocalContext.current
val keyboardController = LocalSoftwareKeyboardController.current
LaunchedEffect(autoFocus) { LaunchedEffect(autoFocus) {
if (autoFocus) { if (autoFocus) {
// 延迟一下,确保输入框已经渲染完成
kotlinx.coroutines.delay(150)
focusRequester.requestFocus() focusRequester.requestFocus()
// 显示键盘
keyboardController?.show()
} }
} }
@@ -83,7 +89,7 @@ fun EditCommentBottomModal(
.fillMaxWidth() .fillMaxWidth()
.weight(1f) .weight(1f)
.clip(RoundedCornerShape(20.dp)) .clip(RoundedCornerShape(20.dp))
.background(Color.Gray.copy(alpha = 0.1f)) .background(AppColors.inputBackground)
.padding(horizontal = 16.dp, vertical = 16.dp) .padding(horizontal = 16.dp, vertical = 16.dp)
) { ) {
Row( Row(
@@ -100,7 +106,7 @@ fun EditCommentBottomModal(
.weight(1f) .weight(1f)
.focusRequester(focusRequester), .focusRequester(focusRequester),
textStyle = TextStyle( textStyle = TextStyle(
color = Color.Black, color = AppColors.text,
fontWeight = FontWeight.Normal fontWeight = FontWeight.Normal
), ),
decorationBox = { innerTextField -> decorationBox = { innerTextField ->

View File

@@ -155,6 +155,17 @@ fun FavouriteListPage() {
) { ) {
items(moments.itemCount) { idx -> items(moments.itemCount) { idx ->
val momentItem = moments[idx] ?: return@items val momentItem = moments[idx] ?: return@items
// 获取缩略图URL优先使用图片如果没有图片则使用视频缩略图
val thumbnailUrl = when {
momentItem.images.isNotEmpty() -> momentItem.images[0].thumbnail
momentItem.videos != null && momentItem.videos.isNotEmpty() -> {
momentItem.videos.first().thumbnailUrl ?: momentItem.videos.first().thumbnailDirectUrl
}
else -> null
}
if (thumbnailUrl == null) return@items
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -169,7 +180,7 @@ fun FavouriteListPage() {
} }
) { ) {
CustomAsyncImage( CustomAsyncImage(
imageUrl = momentItem.images[0].thumbnail, imageUrl = thumbnailUrl,
contentDescription = "", contentDescription = "",
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()

View File

@@ -154,12 +154,12 @@ fun ShortVideoScreen() {
} }
}, },
onCommentClick = { moment -> onCommentClick = { moment ->
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.COMMENT_MOMENT)) { // 点击评论图标只是打开评论弹窗,不应该增加评论数
navController.navigate(NavigationRoute.Login.route) },
} else { onCommentAdded = { moment ->
scope.launch { // 评论添加后的回调,更新评论数
viewModel.onAddComment(moment.id) scope.launch {
} viewModel.onAddComment(moment.id)
} }
}, },
onFavoriteClick = { moment -> onFavoriteClick = { moment ->
@@ -178,6 +178,15 @@ fun ShortVideoScreen() {
onShareClick = { moment -> onShareClick = { moment ->
// TODO: 实现分享功能 // TODO: 实现分享功能
}, },
onAvatarClick = { moment ->
// 点击头像进入用户界面
navController.navigate(
com.aiosman.ravenow.ui.NavigationRoute.AccountProfile.route.replace(
"{id}",
moment.authorId.toString()
)
)
},
onPageChanged = { idx -> currentIndex.value = idx } onPageChanged = { idx -> currentIndex.value = idx }
) )
} }

View File

@@ -19,6 +19,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding 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.shape.RoundedCornerShape
@@ -43,7 +44,9 @@ 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.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
@@ -67,6 +70,7 @@ import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.source.ProgressiveMediaSource import androidx.media3.exoplayer.source.ProgressiveMediaSource
import androidx.media3.ui.AspectRatioFrameLayout import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerView import androidx.media3.ui.PlayerView
import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.R import com.aiosman.ravenow.R
import com.aiosman.ravenow.entity.MomentEntity import com.aiosman.ravenow.entity.MomentEntity
import com.aiosman.ravenow.ui.comment.CommentModalContent import com.aiosman.ravenow.ui.comment.CommentModalContent
@@ -75,6 +79,9 @@ import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
// 激活状态的颜色(点赞/收藏时的红色)
private val ActiveIconColor = Color(0xFFD80264)
@Composable @Composable
fun ShortViewCompose( fun ShortViewCompose(
videoItemsUrl: List<String> = emptyList(), videoItemsUrl: List<String> = emptyList(),
@@ -84,8 +91,10 @@ fun ShortViewCompose(
videoBottom: @Composable ((MomentEntity) -> Unit)? = null, videoBottom: @Composable ((MomentEntity) -> Unit)? = null,
onLikeClick: ((MomentEntity) -> Unit)? = null, onLikeClick: ((MomentEntity) -> Unit)? = null,
onCommentClick: ((MomentEntity) -> Unit)? = null, onCommentClick: ((MomentEntity) -> Unit)? = null,
onCommentAdded: ((MomentEntity) -> Unit)? = null,
onFavoriteClick: ((MomentEntity) -> Unit)? = null, onFavoriteClick: ((MomentEntity) -> Unit)? = null,
onShareClick: ((MomentEntity) -> Unit)? = null, onShareClick: ((MomentEntity) -> Unit)? = null,
onAvatarClick: ((MomentEntity) -> Unit)? = null,
onPageChanged: ((Int) -> Unit)? = null onPageChanged: ((Int) -> Unit)? = null
) { ) {
// 优先使用 videoMoments如果没有则使用 videoItemsUrl // 优先使用 videoMoments如果没有则使用 videoItemsUrl
@@ -159,8 +168,10 @@ fun ShortViewCompose(
VideoBottom = videoBottom, VideoBottom = videoBottom,
onLikeClick = onLikeClick, onLikeClick = onLikeClick,
onCommentClick = onCommentClick, onCommentClick = onCommentClick,
onCommentAdded = onCommentAdded,
onFavoriteClick = onFavoriteClick, onFavoriteClick = onFavoriteClick,
onShareClick = onShareClick onShareClick = onShareClick,
onAvatarClick = onAvatarClick
) )
} }
@@ -183,8 +194,10 @@ private fun SingleVideoItemContent(
VideoBottom: @Composable ((MomentEntity) -> Unit)? = null, VideoBottom: @Composable ((MomentEntity) -> Unit)? = null,
onLikeClick: ((MomentEntity) -> Unit)? = null, onLikeClick: ((MomentEntity) -> Unit)? = null,
onCommentClick: ((MomentEntity) -> Unit)? = null, onCommentClick: ((MomentEntity) -> Unit)? = null,
onCommentAdded: ((MomentEntity) -> Unit)? = null,
onFavoriteClick: ((MomentEntity) -> Unit)? = null, onFavoriteClick: ((MomentEntity) -> Unit)? = null,
onShareClick: ((MomentEntity) -> Unit)? = null onShareClick: ((MomentEntity) -> Unit)? = null,
onAvatarClick: ((MomentEntity) -> Unit)? = null
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
@@ -199,8 +212,10 @@ private fun SingleVideoItemContent(
pauseIconVisibleState = pauseIconVisibleState, pauseIconVisibleState = pauseIconVisibleState,
onLikeClick = onLikeClick, onLikeClick = onLikeClick,
onCommentClick = onCommentClick, onCommentClick = onCommentClick,
onCommentAdded = onCommentAdded,
onFavoriteClick = onFavoriteClick, onFavoriteClick = onFavoriteClick,
onShareClick = onShareClick onShareClick = onShareClick,
onAvatarClick = onAvatarClick
) )
VideoHeader.invoke() VideoHeader.invoke()
if (moment != null && VideoBottom != null) { if (moment != null && VideoBottom != null) {
@@ -228,12 +243,17 @@ fun VideoPlayer(
pauseIconVisibleState: MutableState<Boolean>, pauseIconVisibleState: MutableState<Boolean>,
onLikeClick: ((MomentEntity) -> Unit)? = null, onLikeClick: ((MomentEntity) -> Unit)? = null,
onCommentClick: ((MomentEntity) -> Unit)? = null, onCommentClick: ((MomentEntity) -> Unit)? = null,
onCommentAdded: ((MomentEntity) -> Unit)? = null,
onFavoriteClick: ((MomentEntity) -> Unit)? = null, onFavoriteClick: ((MomentEntity) -> Unit)? = null,
onShareClick: ((MomentEntity) -> Unit)? = null, onShareClick: ((MomentEntity) -> Unit)? = null,
onAvatarClick: ((MomentEntity) -> Unit)? = null,
) { ) {
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val lifecycleOwner = LocalLifecycleOwner.current val lifecycleOwner = LocalLifecycleOwner.current
val configuration = androidx.compose.ui.platform.LocalConfiguration.current
val screenHeight = configuration.screenHeightDp.dp
val sheetHeight = screenHeight * 0.7f // 屏幕的一大半高度
var showCommentModal by remember { mutableStateOf(false) } var showCommentModal by remember { mutableStateOf(false) }
var sheetState = rememberModalBottomSheetState( var sheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true skipPartiallyExpanded = true
@@ -371,42 +391,51 @@ fun VideoPlayer(
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
if (moment != null) { if (moment != null) {
UserAvatar(avatarUrl = moment.avatar) // 使用 key 确保状态变化时重新组合
VideoBtn( androidx.compose.runtime.key(moment.id, moment.isFavorite) {
icon = R.drawable.rider_pro_video_like, UserAvatar(
text = formatCount(moment.likeCount) avatarUrl = moment.avatar,
) { onClick = { onAvatarClick?.invoke(moment) }
moment?.let { onLikeClick?.invoke(it) } )
} VideoBtn(
VideoBtn( icon = if (moment.liked) R.drawable.rider_pro_moment_liked else R.drawable.rider_pro_moment_like,
icon = R.drawable.rider_pro_video_comment, text = formatCount(moment.likeCount),
text = formatCount(moment.commentCount) isActive = moment.liked,
) { onClick = { onLikeClick?.invoke(moment) }
moment?.let { )
showCommentModal = true VideoBtn(
onCommentClick?.invoke(it) icon = R.mipmap.icon_comment,
} text = formatCount(moment.commentCount),
} onClick = {
VideoBtn( showCommentModal = true
icon = R.drawable.rider_pro_video_favor, onCommentClick?.invoke(moment)
text = formatCount(moment.favoriteCount) }
) { )
moment?.let { onFavoriteClick?.invoke(it) } VideoBtn(
} icon = if (moment.isFavorite) R.mipmap.icon_variant_2 else R.mipmap.icon_collect,
VideoBtn( text = formatCount(moment.favoriteCount),
icon = R.drawable.rider_pro_video_share, isActive = false, // 收藏后不使用红色滤镜,保持图标原本颜色
text = formatCount(moment.shareCount) keepOriginalColor = moment.isFavorite, // 收藏后保持原始颜色
) { onClick = { onFavoriteClick?.invoke(moment) }
moment?.let { onShareClick?.invoke(it) } )
VideoBtn(
icon = R.mipmap.icon_share,
text = formatCount(moment.shareCount),
onClick = { onShareClick?.invoke(moment) }
)
} }
} else { } else {
UserAvatar() UserAvatar()
VideoBtn(icon = R.drawable.rider_pro_video_like, text = "0") VideoBtn(icon = R.drawable.rider_pro_moment_like, text = "0")
VideoBtn(icon = R.drawable.rider_pro_video_comment, text = "0") { VideoBtn(
showCommentModal = true icon = R.mipmap.icon_comment,
} text = "0",
VideoBtn(icon = R.drawable.rider_pro_video_favor, text = "0") onClick = {
VideoBtn(icon = R.drawable.rider_pro_video_share, text = "0") showCommentModal = true
}
)
VideoBtn(icon = R.mipmap.icon_collect, text = "0")
VideoBtn(icon = R.mipmap.icon_share, text = "0")
} }
} }
} }
@@ -465,26 +494,44 @@ fun VideoPlayer(
} }
if (showCommentModal && moment != null) { if (showCommentModal && moment != null) {
val AppColors = LocalAppTheme.current
ModalBottomSheet( ModalBottomSheet(
onDismissRequest = { showCommentModal = false }, onDismissRequest = { showCommentModal = false },
containerColor = Color.White, containerColor = AppColors.background,
sheetState = sheetState sheetState = sheetState,
modifier = Modifier
.fillMaxWidth()
.height(sheetHeight)
) { ) {
CommentModalContent(postId = moment.id) { CommentModalContent(
postId = moment.id,
} commentCount = moment.commentCount,
onCommentAdded = {
onCommentAdded?.invoke(moment)
}
)
} }
} }
} }
@Composable @Composable
fun UserAvatar(avatarUrl: String? = null) { fun UserAvatar(
avatarUrl: String? = null,
onClick: (() -> Unit)? = null
) {
Box( Box(
modifier = Modifier modifier = Modifier
.padding(bottom = 16.dp) .padding(bottom = 16.dp)
.size(40.dp) .size(40.dp)
.border(width = 3.dp, color = Color.White, shape = RoundedCornerShape(40.dp)) .border(width = 3.dp, color = Color.White, shape = RoundedCornerShape(40.dp))
.clip(RoundedCornerShape(40.dp)) .clip(RoundedCornerShape(40.dp))
.then(
if (onClick != null) {
Modifier.noRippleClickable { onClick() }
} else {
Modifier
}
)
) { ) {
if (avatarUrl != null && avatarUrl.isNotEmpty()) { if (avatarUrl != null && avatarUrl.isNotEmpty()) {
CustomAsyncImage( CustomAsyncImage(
@@ -512,19 +559,31 @@ private fun formatCount(count: Int): String {
} }
@Composable @Composable
fun VideoBtn(@DrawableRes icon: Int, text: String, onClick: (() -> Unit)? = null) { fun VideoBtn(
@DrawableRes icon: Int,
text: String,
onClick: (() -> Unit)? = null,
isActive: Boolean = false,
keepOriginalColor: Boolean = false // 是否保持原始颜色(不应用白色滤镜)
) {
Column( Column(
modifier = Modifier modifier = Modifier
.padding(bottom = 16.dp) .padding(bottom = 16.dp)
.clickable { .noRippleClickable {
onClick?.invoke() onClick?.invoke()
}, },
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Image( Image(
modifier = Modifier.size(36.dp), modifier = Modifier.size(30.dp),
painter = painterResource(id = icon), painter = painterResource(id = icon),
contentDescription = "" contentDescription = "",
contentScale = ContentScale.FillBounds, // 填满容器,让图标看起来更大
colorFilter = when {
isActive -> ColorFilter.tint(ActiveIconColor)
keepOriginalColor -> null // 保持原始颜色
else -> ColorFilter.tint(Color.White) // 未激活状态时图标为白色
}
) )
Text( Text(
text = text, text = text,

Binary file not shown.

After

Width:  |  Height:  |  Size: 970 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 647 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 805 B