修改若干bug、调整动态标签栏、添加文本等

修复修改用户头像以及壁纸不保存,头像仍然修改成功

动态-新闻界面暗色模式调整

新增动态标签栏选中标签时会有文本变大效果

搜索后的动态界面增加点赞/收藏/评论/转发动态按钮

修复搜索智能体后点击智能体不能跳转至智能体主页

修复进入消息-全部列表,消息界面显示为空

我的派币-历史记录添加文本资源
This commit is contained in:
2025-11-14 17:30:51 +08:00
parent 6d38b3c549
commit f981efd58d
14 changed files with 416 additions and 127 deletions

View File

@@ -1,6 +1,8 @@
package com.aiosman.ravenow.data package com.aiosman.ravenow.data
import android.content.Context
import com.aiosman.ravenow.AppStore import com.aiosman.ravenow.AppStore
import com.aiosman.ravenow.R
import com.aiosman.ravenow.data.api.ApiClient import com.aiosman.ravenow.data.api.ApiClient
import com.aiosman.ravenow.data.api.DictItem import com.aiosman.ravenow.data.api.DictItem
import com.aiosman.ravenow.data.api.PointsBalance import com.aiosman.ravenow.data.api.PointsBalance
@@ -370,27 +372,29 @@ object PointService {
} }
/** /**
* 获取变更原因的中文描述 * 获取变更原因的描述(支持多语言)
* *
* @param context Context 用于获取资源
* @param reason 变更原因代码 * @param reason 变更原因代码
* @return 中文描述 * @return 本地化描述
*/ */
fun getReasonDescription(reason: String): String { fun getReasonDescription(context: Context, reason: String): String {
return when (reason) { val resourceId = when (reason) {
ChangeReason.EARN_REGISTER -> "新用户注册奖励" ChangeReason.EARN_REGISTER -> R.string.earn_register
ChangeReason.EARN_DAILY -> "每日签到奖励" ChangeReason.EARN_DAILY -> R.string.earn_daily
ChangeReason.EARN_TASK -> "任务完成奖励" ChangeReason.EARN_TASK -> R.string.earn_task
ChangeReason.EARN_INVITE -> "邀请好友奖励" ChangeReason.EARN_INVITE -> R.string.earn_invite
ChangeReason.EARN_RECHARGE -> "充值获得" ChangeReason.EARN_RECHARGE -> R.string.earn_recharge
ChangeReason.SPEND_GROUP_CREATE -> "创建群聊" ChangeReason.SPEND_GROUP_CREATE -> R.string.spend_group_create
ChangeReason.SPEND_GROUP_EXPAND -> "扩容群聊" ChangeReason.SPEND_GROUP_EXPAND -> R.string.spend_group_expand
ChangeReason.SPEND_AGENT_PRIVATE -> "Agent 私密模式" ChangeReason.SPEND_AGENT_PRIVATE -> R.string.spend_agent_private
ChangeReason.SPEND_AGENT_MEMORY -> "Agent 记忆添加" ChangeReason.SPEND_AGENT_MEMORY -> R.string.spend_agent_memory
ChangeReason.SPEND_ROOM_MEMORY -> "房间记忆添加" ChangeReason.SPEND_ROOM_MEMORY -> R.string.spend_room_memory
ChangeReason.SPEND_CHAT_BACKGROUND -> "自定义聊天背景" ChangeReason.SPEND_CHAT_BACKGROUND -> R.string.spend_chat_background
ChangeReason.SPEND_SCHEDULE_EVENT -> "定时事件解锁" ChangeReason.SPEND_SCHEDULE_EVENT -> R.string.spend_schedule_event
else -> reason // 未知原因,返回原始代码 else -> null
} }
return resourceId?.let { context.getString(it) } ?: reason // 未知原因,返回原始代码
} }
/** /**

View File

@@ -31,7 +31,12 @@ object AccountEditViewModel : ViewModel() {
// 本地扩展字段 // 本地扩展字段
var mbti by mutableStateOf<String?>(null) var mbti by mutableStateOf<String?>(null)
var zodiac by mutableStateOf<String?>(null) var zodiac by mutableStateOf<String?>(null)
suspend fun reloadProfile(updateTrtcProfile:Boolean = false) { // 保存原始值,用于取消时恢复
private var originalName: String = ""
private var originalBio: String = ""
private var originalMbti: String? = null
private var originalZodiac: String? = null
suspend fun reloadProfile(updateTrtcProfile:Boolean = false, clearCroppedBitmap: Boolean = false) {
Log.d("AccountEditViewModel", "reloadProfile: 开始加载用户资料") Log.d("AccountEditViewModel", "reloadProfile: 开始加载用户资料")
isLoading = true isLoading = true
try { try {
@@ -41,13 +46,23 @@ object AccountEditViewModel : ViewModel() {
profile = it profile = it
name = it.nickName name = it.nickName
bio = it.bio bio = it.bio
// 清除之前裁剪的图片 // 保存原始值,用于取消时恢复
croppedBitmap = null originalName = it.nickName
originalBio = it.bio
// 只在明确要求时清除之前裁剪的图片(例如保存成功后)
if (clearCroppedBitmap) {
croppedBitmap = null
}
// 读取本地扩展字段 // 读取本地扩展字段
try { try {
val uid = it.id // 使用 profile 的 id确保非空 val uid = it.id // 使用 profile 的 id确保非空
mbti = com.aiosman.ravenow.AppStore.getUserMbti(uid) val loadedMbti = com.aiosman.ravenow.AppStore.getUserMbti(uid)
zodiac = com.aiosman.ravenow.AppStore.getUserZodiac(uid) val loadedZodiac = com.aiosman.ravenow.AppStore.getUserZodiac(uid)
mbti = loadedMbti
zodiac = loadedZodiac
// 保存原始值
originalMbti = loadedMbti
originalZodiac = loadedZodiac
} catch (_: Exception) { } } catch (_: Exception) { }
if (updateTrtcProfile) { if (updateTrtcProfile) {
TrtcHelper.updateTrtcProfile( TrtcHelper.updateTrtcProfile(
@@ -69,12 +84,15 @@ object AccountEditViewModel : ViewModel() {
} }
fun resetToOriginalData() { fun resetToOriginalData() {
profile?.let { // 恢复所有字段到原始值
name = it.nickName name = originalName
bio = it.bio bio = originalBio
// 清除之前裁剪的图片 mbti = originalMbti
croppedBitmap = null zodiac = originalZodiac
} // 清除之前裁剪的图片和壁纸
croppedBitmap = null
bannerImageUrl = null
bannerFile = null
} }
@@ -129,8 +147,8 @@ object AccountEditViewModel : ViewModel() {
// 清除背景图状态 // 清除背景图状态
bannerImageUrl = null bannerImageUrl = null
bannerFile = null bannerFile = null
// 刷新用户资料 // 刷新用户资料,保存成功后清除裁剪的图片
reloadProfile() reloadProfile(clearCroppedBitmap = true)
// 刷新个人资料页面的用户资料 // 刷新个人资料页面的用户资料
MyProfileViewModel.loadUserProfile() MyProfileViewModel.loadUserProfile()
} }

View File

@@ -74,6 +74,7 @@ import com.aiosman.ravenow.ConstVars
import com.aiosman.ravenow.ui.composables.pickupAndCompressLauncher import com.aiosman.ravenow.ui.composables.pickupAndCompressLauncher
import android.widget.Toast import android.widget.Toast
import java.io.File import java.io.File
import androidx.activity.compose.BackHandler
/** /**
* 编辑用户资料界面 * 编辑用户资料界面
@@ -171,6 +172,13 @@ fun AccountEditScreen2(onUpdateBanner: ((Uri, File, Context) -> Unit)? = null,)
model.reloadProfile() model.reloadProfile()
} }
// 处理系统返回键
BackHandler {
// 用户未保存直接返回,恢复所有字段到原始值
model.resetToOriginalData()
navController.navigateUp()
}
// 设置状态栏为透明,根据暗色模式决定图标颜色 // 设置状态栏为透明,根据暗色模式决定图标颜色
val systemUiController = rememberSystemUiController() val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
@@ -291,6 +299,8 @@ fun AccountEditScreen2(onUpdateBanner: ((Uri, File, Context) -> Unit)? = null,)
.clip(CircleShape) .clip(CircleShape)
.background(Color.White.copy(alpha = 0.3f)) .background(Color.White.copy(alpha = 0.3f))
.noRippleClickable { .noRippleClickable {
// 用户未保存直接返回,恢复所有字段到原始值
model.resetToOriginalData()
navController.navigateUp() navController.navigateUp()
} }
.align(Alignment.CenterStart), .align(Alignment.CenterStart),

View File

@@ -6,8 +6,10 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
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.height
import androidx.compose.foundation.layout.offset
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.layout.width import androidx.compose.foundation.layout.width
@@ -15,10 +17,14 @@ import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment 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.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
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.aiosman.ravenow.LocalAppTheme import com.aiosman.ravenow.LocalAppTheme
@@ -74,6 +80,18 @@ fun UnderlineTabItem(
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
// 动画化字体大小和padding
val animatedFontSize by animateFloatAsState(
targetValue = if (isSelected) 17f else 15f,
animationSpec = tween(durationMillis = 200),
label = "fontSize"
)
val animatedPadding by animateDpAsState(
targetValue = if (isSelected) 20.dp else 16.dp,
animationSpec = tween(durationMillis = 200),
label = "padding"
)
Column( Column(
modifier = modifier modifier = modifier
@@ -81,24 +99,33 @@ fun UnderlineTabItem(
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Text( Box(
text = text, modifier = Modifier
fontSize = 15.sp, .padding(horizontal = animatedPadding)
fontWeight = FontWeight.ExtraBold, .padding(top = 13.dp, bottom = 0.dp),
color = if (isSelected) AppColors.text else AppColors.text.copy(alpha = 0.6f), contentAlignment = Alignment.BottomCenter
modifier = Modifier.padding(horizontal = 16.dp).padding(top = 13.dp) ) {
) Text(
text = text,
fontSize = animatedFontSize.sp,
fontWeight = FontWeight.ExtraBold,
color = if (isSelected) AppColors.text else AppColors.text.copy(alpha = 0.6f),
textAlign = androidx.compose.ui.text.style.TextAlign.Center
)
}
// 选中状态下显示图标 // 选中状态下显示图标
Box( if (isSelected) {
modifier = Modifier.size(24.dp), Box(
contentAlignment = Alignment.Center modifier = Modifier
) { .size(24.dp)
if (isSelected) { .offset(y = (-4).dp),
contentAlignment = Alignment.Center
) {
Image( Image(
painter = painterResource(id = R.mipmap.underline), painter = painterResource(id = R.mipmap.underline),
contentDescription = "selected indicator", contentDescription = "selected indicator",
modifier = Modifier.fillMaxSize()
) )
} }
} }

View File

@@ -72,6 +72,8 @@ fun ImageCropScreen() {
} }
} }
if (uri == null) { if (uri == null) {
// 用户取消图片选择,清除已裁剪的图片
AccountEditViewModel.croppedBitmap = null
navController.popBackStack() navController.popBackStack()
} }
} }
@@ -103,6 +105,8 @@ fun ImageCropScreen() {
painter = painterResource(R.drawable.rider_pro_back_icon), painter = painterResource(R.drawable.rider_pro_back_icon),
contentDescription = null, contentDescription = null,
modifier = Modifier.clickable { modifier = Modifier.clickable {
// 用户取消头像选择,清除已裁剪的图片
AccountEditViewModel.croppedBitmap = null
navController.popBackStack() navController.popBackStack()
}, },
colorFilter = ColorFilter.tint(Color.White) colorFilter = ColorFilter.tint(Color.White)
@@ -119,11 +123,9 @@ fun ImageCropScreen() {
val bitmap = it.onCrop() val bitmap = it.onCrop()
// 专门处理个人资料头像 // 专门处理个人资料头像
// 只设置裁剪后的图片,不立即上传,等待用户在编辑资料界面点击保存
AccountEditViewModel.croppedBitmap = bitmap AccountEditViewModel.croppedBitmap = bitmap
AccountEditViewModel.viewModelScope.launch { navController.popBackStack()
AccountEditViewModel.updateUserProfile(context)
navController.popBackStack()
}
} }
} }
) )

View File

@@ -121,35 +121,88 @@ fun AllChatListScreen() {
var isLoading by remember { mutableStateOf(false) } var isLoading by remember { mutableStateOf(false) }
var error by remember { mutableStateOf<String?>(null) } var error by remember { mutableStateOf<String?>(null) }
// 监听各个 ViewModel 的列表变化
val agentChatList = AgentChatListViewModel.agentChatList
val groupChatList = GroupChatListViewModel.groupChatList
val friendChatList = FriendChatListViewModel.friendChatList
// 当列表变化时,自动更新合并列表
LaunchedEffect(agentChatList, groupChatList, friendChatList) {
val combinedList = mutableListOf<CombinedConversation>()
agentChatList.forEach { agent ->
combinedList.add(CombinedConversation(type = "agent", agentConversation = agent))
}
groupChatList.forEach { group ->
combinedList.add(CombinedConversation(type = "group", groupConversation = group))
}
friendChatList.forEach { friend ->
val isDuplicate = combinedList.any {
it.type == "agent" && it.agentConversation?.trtcUserId == friend.trtcUserId
}
if (!isDuplicate) {
combinedList.add(CombinedConversation(type = "friend", friendConversation = friend))
}
}
// 按最后消息时间排序
val sortedList = combinedList.sortedByDescending {
it.lastMessageTime
}
allConversations = sortedList
}
// 监听加载状态
val isAnyLoading = AgentChatListViewModel.isLoading ||
GroupChatListViewModel.isLoading ||
FriendChatListViewModel.isLoading
// 当加载状态变化时,更新 isLoading
LaunchedEffect(isAnyLoading) {
if (isAnyLoading) {
// 如果有任何数据正在加载,确保显示加载状态
if (!isLoading) {
isLoading = true
}
} else {
// 所有数据加载完成
if (isLoading) {
isLoading = false
}
}
}
val state = rememberPullRefreshState( val state = rememberPullRefreshState(
refreshing = refreshing, refreshing = refreshing,
onRefresh = { onRefresh = {
refreshing = true refreshing = true
refreshAllData(context, // 刷新所有类型的数据
onSuccess = { conversations -> AgentChatListViewModel.refreshPager(pullRefresh = true, context = context)
allConversations = conversations GroupChatListViewModel.refreshPager(pullRefresh = true, context = context)
refreshing = false FriendChatListViewModel.refreshPager(pullRefresh = true, context = context)
},
onError = { errorMsg ->
error = errorMsg
refreshing = false
}
)
} }
) )
// 监听刷新状态
LaunchedEffect(AgentChatListViewModel.refreshing, GroupChatListViewModel.refreshing, FriendChatListViewModel.refreshing) {
val isAnyRefreshing = AgentChatListViewModel.refreshing ||
GroupChatListViewModel.refreshing ||
FriendChatListViewModel.refreshing
if (!isAnyRefreshing && refreshing) {
refreshing = false
}
}
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
isLoading = true isLoading = true
refreshAllData(context, // 初始化加载所有类型的数据
onSuccess = { conversations -> AgentChatListViewModel.refreshPager(context = context)
allConversations = conversations GroupChatListViewModel.refreshPager(context = context)
isLoading = false FriendChatListViewModel.refreshPager(context = context)
},
onError = { errorMsg ->
error = errorMsg
isLoading = false
}
)
} }
Column( Column(
@@ -235,16 +288,10 @@ fun AllChatListScreen() {
ReloadButton( ReloadButton(
onClick = { onClick = {
isLoading = true isLoading = true
refreshAllData(context, // 重新加载所有类型的数据
onSuccess = { conversations -> AgentChatListViewModel.refreshPager(context = context)
allConversations = conversations GroupChatListViewModel.refreshPager(context = context)
isLoading = false FriendChatListViewModel.refreshPager(context = context)
},
onError = { errorMsg ->
error = errorMsg
isLoading = false
}
)
} }
) )
} }
@@ -363,44 +410,3 @@ fun AllChatListScreen() {
} }
} }
fun refreshAllData(
context: android.content.Context,
onSuccess: (List<CombinedConversation>) -> Unit,
onError: (String) -> Unit
) {
try {
// 同时刷新所有类型的数据
AgentChatListViewModel.refreshPager(context = context)
GroupChatListViewModel.refreshPager(context = context)
FriendChatListViewModel.refreshPager(context = context)
val combinedList = mutableListOf<CombinedConversation>()
AgentChatListViewModel.agentChatList.forEach { agent ->
combinedList.add(CombinedConversation(type = "agent", agentConversation = agent))
}
GroupChatListViewModel.groupChatList.forEach { group ->
combinedList.add(CombinedConversation(type = "group", groupConversation = group))
}
FriendChatListViewModel.friendChatList.forEach { friend ->
val isDuplicate = combinedList.any {//判断重复
it.type == "agent" && it.agentConversation?.trtcUserId == friend.trtcUserId
}
if (!isDuplicate) {
combinedList.add(CombinedConversation(type = "friend", friendConversation = friend))
}
}
// 按最后消息时间排序
val sortedList = combinedList.sortedByDescending {
it.lastMessageTime
}
onSuccess(sortedList)
} catch (e: Exception) {
onError("刷新数据失败: ${e.message}")
}
}

View File

@@ -104,7 +104,7 @@ fun MomentsList(
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(44.dp) .height(54.dp)
.padding(horizontal = 16.dp), .padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically

View File

@@ -390,7 +390,8 @@ fun NewsItem(
NewsActionButton( NewsActionButton(
icon = if (moment.isFavorite) R.mipmap.icon_variant_2 else R.mipmap.icon_collect, icon = if (moment.isFavorite) R.mipmap.icon_variant_2 else R.mipmap.icon_collect,
count = moment.favoriteCount.toString(), count = moment.favoriteCount.toString(),
isActive = moment.isFavorite, isActive = false, // 收藏后不使用红色滤镜,保持图标原本颜色
keepOriginalColor = moment.isFavorite, // 收藏后保持原始颜色
modifier = Modifier.noRippleClickable { onFavoriteClick() } modifier = Modifier.noRippleClickable { onFavoriteClick() }
) )
@@ -406,6 +407,9 @@ fun NewsItem(
} }
} }
// 激活状态的颜色(点赞/收藏时的红色)
private val ActiveIconColor = Color(0xFFD80264)
// 互动栏按钮 // 互动栏按钮
@Composable @Composable
fun NewsActionButton( fun NewsActionButton(
@@ -414,7 +418,8 @@ fun NewsActionButton(
isActive: Boolean, isActive: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
text: String? = null, text: String? = null,
textSize: androidx.compose.ui.unit.TextUnit = 12.sp textSize: androidx.compose.ui.unit.TextUnit = 12.sp,
keepOriginalColor: Boolean = false // 是否保持原始颜色(不应用颜色滤镜)
) { ) {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
@@ -432,7 +437,12 @@ fun NewsActionButton(
Image( Image(
painter = androidx.compose.ui.res.painterResource(id = icon), painter = androidx.compose.ui.res.painterResource(id = icon),
contentDescription = "操作图标", contentDescription = "操作图标",
modifier = Modifier.size(16.dp) modifier = Modifier.size(16.dp),
colorFilter = when {
isActive -> ColorFilter.tint(ActiveIconColor)
keepOriginalColor -> null // 保持原始颜色
else -> ColorFilter.tint(AppColors.text) // 未激活状态时使用文字颜色适配暗色模式
}
) )
if (count.isNotEmpty()) { if (count.isNotEmpty()) {

View File

@@ -69,14 +69,18 @@ import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.LocalNavController import com.aiosman.ravenow.LocalNavController
import com.aiosman.ravenow.R import com.aiosman.ravenow.R
import com.aiosman.ravenow.entity.AccountProfileEntity import com.aiosman.ravenow.entity.AccountProfileEntity
import com.aiosman.ravenow.GuestLoginCheckOut
import com.aiosman.ravenow.GuestLoginCheckOutScene
import com.aiosman.ravenow.ui.composables.CustomAsyncImage import com.aiosman.ravenow.ui.composables.CustomAsyncImage
import com.aiosman.ravenow.ui.composables.MomentCard import com.aiosman.ravenow.ui.composables.MomentCard
import com.aiosman.ravenow.ui.composables.TabItem import com.aiosman.ravenow.ui.composables.TabItem
import com.aiosman.ravenow.ui.composables.TabSpacer import com.aiosman.ravenow.ui.composables.TabSpacer
import com.aiosman.ravenow.ui.composables.rememberDebouncer
import com.aiosman.ravenow.ui.index.tabs.message.tab.AgentChatListViewModel import com.aiosman.ravenow.ui.index.tabs.message.tab.AgentChatListViewModel
import com.aiosman.ravenow.ui.index.tabs.profile.composable.RoomItem import com.aiosman.ravenow.ui.index.tabs.profile.composable.RoomItem
import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import com.aiosman.ravenow.ui.navigateToGroupChat import com.aiosman.ravenow.ui.navigateToGroupChat
import com.aiosman.ravenow.ui.NavigationRoute
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import com.aiosman.ravenow.utils.NetworkUtils import com.aiosman.ravenow.utils.NetworkUtils
@@ -403,6 +407,8 @@ fun MomentResultTab() {
var moments = dataFlow.collectAsLazyPagingItems() var moments = dataFlow.collectAsLazyPagingItems()
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope()
val navController = LocalNavController.current
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@@ -473,6 +479,12 @@ fun MomentResultTab() {
) { ) {
items(moments.itemCount) { idx -> items(moments.itemCount) { idx ->
val momentItem = moments[idx] ?: return@items val momentItem = moments[idx] ?: return@items
val commentDebouncer = rememberDebouncer()
val likeDebouncer = rememberDebouncer()
val favoriteDebouncer = rememberDebouncer()
val followDebouncer = rememberDebouncer()
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -480,10 +492,61 @@ fun MomentResultTab() {
) { ) {
MomentCard( MomentCard(
momentEntity = momentItem, momentEntity = momentItem,
hideAction = true, onAddComment = {
commentDebouncer {
// 检查游客模式,如果是游客则跳转登录
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.COMMENT_MOMENT)) {
navController.navigate(NavigationRoute.Login.route)
} else {
scope.launch {
model.onAddComment(momentItem.id)
}
}
}
},
onLikeClick = {
likeDebouncer {
// 检查游客模式,如果是游客则跳转登录
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.LIKE_MOMENT)) {
navController.navigate(NavigationRoute.Login.route)
} else {
scope.launch {
if (momentItem.liked) {
model.dislikeMoment(momentItem.id)
} else {
model.likeMoment(momentItem.id)
}
}
}
}
},
onFavoriteClick = {
favoriteDebouncer {
// 检查游客模式,如果是游客则跳转登录
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.LIKE_MOMENT)) {
navController.navigate(NavigationRoute.Login.route)
} else {
scope.launch {
if (momentItem.isFavorite) {
model.unfavoriteMoment(momentItem.id)
} else {
model.favoriteMoment(momentItem.id)
}
}
}
}
},
onFollowClick = { onFollowClick = {
model.momentFollowAction(momentItem) followDebouncer {
} // 检查游客模式,如果是游客则跳转登录
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.FOLLOW_USER)) {
navController.navigate(NavigationRoute.Login.route)
} else {
model.momentFollowAction(momentItem)
}
}
},
showFollowButton = true
) )
} }
// Spacer(modifier = Modifier.padding(16.dp)) // Spacer(modifier = Modifier.padding(16.dp))
@@ -663,6 +726,8 @@ fun AiResultTab() {
val context = LocalContext.current val context = LocalContext.current
val model = SearchViewModel val model = SearchViewModel
val agents = model.agentsFlow.collectAsLazyPagingItems() val agents = model.agentsFlow.collectAsLazyPagingItems()
val navController = LocalNavController.current
val scope = rememberCoroutineScope()
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@@ -736,7 +801,23 @@ fun AiResultTab() {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(16.dp), .padding(16.dp)
.noRippleClickable {
scope.launch {
try {
val userService = com.aiosman.ravenow.data.UserServiceImpl()
val profile = userService.getUserProfileByOpenId(agent.openId)
navController.navigate(
NavigationRoute.AccountProfile.route
.replace("{id}", profile.id.toString())
.replace("{isAiAccount}", "true")
)
} catch (e: Exception) {
// 处理错误
e.printStackTrace()
}
}
},
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
CustomAsyncImage( CustomAsyncImage(

View File

@@ -200,4 +200,88 @@ object SearchViewModel : ViewModel() {
} }
} }
} }
suspend fun likeMoment(id: Int) {
try {
momentService.likeMoment(id)
updateMomentLike(id, true)
} catch (e: Exception) {
e.printStackTrace()
}
}
suspend fun dislikeMoment(id: Int) {
try {
momentService.dislikeMoment(id)
updateMomentLike(id, false)
} catch (e: Exception) {
e.printStackTrace()
}
}
suspend fun favoriteMoment(id: Int) {
try {
momentService.favoriteMoment(id)
updateMomentFavorite(id, true)
} catch (e: Exception) {
e.printStackTrace()
}
}
suspend fun unfavoriteMoment(id: Int) {
try {
momentService.unfavoriteMoment(id)
updateMomentFavorite(id, false)
} catch (e: Exception) {
e.printStackTrace()
}
}
fun onAddComment(id: Int) {
updateMomentCommentCount(id, 1)
}
private fun updateMomentLike(id: Int, isLike: Boolean) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(
liked = isLike,
likeCount = momentItem.likeCount + if (isLike) 1 else -1
)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
private fun updateMomentFavorite(id: Int, isFavorite: Boolean) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(
isFavorite = isFavorite,
favoriteCount = momentItem.favoriteCount + if (isFavorite) 1 else -1
)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
private fun updateMomentCommentCount(id: Int, delta: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(
commentCount = (momentItem.commentCount + delta).coerceAtLeast(0)
)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
} }

View File

@@ -57,6 +57,7 @@ import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -453,6 +454,7 @@ private fun PointsHistoryList(
hasNext: Boolean hasNext: Boolean
) { ) {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
val context = LocalContext.current
val numberFormat = remember { NumberFormat.getNumberInstance(Locale.getDefault()) } val numberFormat = remember { NumberFormat.getNumberInstance(Locale.getDefault()) }
val listState = rememberLazyListState() val listState = rememberLazyListState()
val loading = PointsViewModel.loading val loading = PointsViewModel.loading
@@ -544,7 +546,7 @@ private fun PointsHistoryList(
) )
Spacer(Modifier.size(12.dp)) Spacer(Modifier.size(12.dp))
Column { Column {
Text(text = PointService.getReasonDescription(item.reason ?: "" ), color = AppColors.text, fontSize = 16.sp, fontWeight = FontWeight.W600) Text(text = PointService.getReasonDescription(context, item.reason ?: "" ), color = AppColors.text, fontSize = 16.sp, fontWeight = FontWeight.W600)
Spacer(Modifier.height(4.dp)) Spacer(Modifier.height(4.dp))
Text(text = item.createdAt ?: "", color = AppColors.secondaryText, fontSize = 12.sp) Text(text = item.createdAt ?: "", color = AppColors.secondaryText, fontSize = 12.sp)
} }

View File

@@ -362,6 +362,21 @@
<string name="complete_tasks_desc">タスクを完了して報酬を獲得</string> <string name="complete_tasks_desc">タスクを完了して報酬を獲得</string>
<string name="recharge_pai_coin">パイコインをチャージ</string> <string name="recharge_pai_coin">パイコインをチャージ</string>
<string name="recharge_pai_coin_desc">複数のパッケージが利用可能、今すぐチャージ</string> <string name="recharge_pai_coin_desc">複数のパッケージが利用可能、今すぐチャージ</string>
<!-- Transaction History Reasons -->
<string name="earn_register">新規ユーザー登録報酬</string>
<string name="earn_daily">デイリーチェックイン報酬</string>
<string name="earn_task">タスク完了報酬</string>
<string name="earn_invite">友達招待報酬</string>
<string name="earn_recharge">チャージ</string>
<string name="spend_group_create">グループチャット作成</string>
<string name="spend_group_expand">グループチャット拡張</string>
<string name="spend_agent_private">エージェントプライベートモード</string>
<string name="spend_agent_memory">エージェント記憶追加</string>
<string name="spend_room_memory">ルーム記憶追加</string>
<string name="spend_chat_background">カスタムチャット背景</string>
<string name="spend_schedule_event">スケジュールイベント解除</string>
<string name="create_group_chat_failed">グループチャットの作成に失敗しました: %1$s</string> <string name="create_group_chat_failed">グループチャットの作成に失敗しました: %1$s</string>
<string name="connect_world_start_following">世界をつなぐ、フォローから始めましょう</string> <string name="connect_world_start_following">世界をつなぐ、フォローから始めましょう</string>
<string name="why_not_start_with_agent">エージェントから世界を知り始めませんか?</string> <string name="why_not_start_with_agent">エージェントから世界を知り始めませんか?</string>

View File

@@ -367,6 +367,21 @@
<string name="complete_tasks_desc">完成任务可获得奖励</string> <string name="complete_tasks_desc">完成任务可获得奖励</string>
<string name="recharge_pai_coin">充值派币</string> <string name="recharge_pai_coin">充值派币</string>
<string name="recharge_pai_coin_desc">多种套餐可选,立即充值</string> <string name="recharge_pai_coin_desc">多种套餐可选,立即充值</string>
<!-- Transaction History Reasons -->
<string name="earn_register">新用户注册奖励</string>
<string name="earn_daily">每日签到奖励</string>
<string name="earn_task">任务完成奖励</string>
<string name="earn_invite">邀请好友奖励</string>
<string name="earn_recharge">充值获得</string>
<string name="spend_group_create">创建群聊</string>
<string name="spend_group_expand">扩容群聊</string>
<string name="spend_agent_private">Agent 私密模式</string>
<string name="spend_agent_memory">Agent 记忆添加</string>
<string name="spend_room_memory">房间记忆添加</string>
<string name="spend_chat_background">自定义聊天背景</string>
<string name="spend_schedule_event">定时事件解锁</string>
<string name="create_group_chat_failed">创建群聊失败: %1$s</string> <string name="create_group_chat_failed">创建群聊失败: %1$s</string>
<string name="create_group_chat_confirm_title">创建群聊确认</string> <string name="create_group_chat_confirm_title">创建群聊确认</string>
<string name="create_group_chat_required_cost">需要消耗:</string> <string name="create_group_chat_required_cost">需要消耗:</string>

View File

@@ -361,6 +361,21 @@
<string name="complete_tasks_desc">Complete tasks to earn rewards</string> <string name="complete_tasks_desc">Complete tasks to earn rewards</string>
<string name="recharge_pai_coin">Recharge Pai Coin</string> <string name="recharge_pai_coin">Recharge Pai Coin</string>
<string name="recharge_pai_coin_desc">Multiple packages available, recharge now</string> <string name="recharge_pai_coin_desc">Multiple packages available, recharge now</string>
<!-- Transaction History Reasons -->
<string name="earn_register">New User Registration Reward</string>
<string name="earn_daily">Daily Check-in Reward</string>
<string name="earn_task">Task Completion Reward</string>
<string name="earn_invite">Invite Friends Reward</string>
<string name="earn_recharge">Recharge</string>
<string name="spend_group_create">Create Group Chat</string>
<string name="spend_group_expand">Expand Group Chat</string>
<string name="spend_agent_private">Agent Private Mode</string>
<string name="spend_agent_memory">Agent Memory Added</string>
<string name="spend_room_memory">Room Memory Added</string>
<string name="spend_chat_background">Custom Chat Background</string>
<string name="spend_schedule_event">Scheduled Event Unlocked</string>
<string name="create_group_chat_failed">Failed to create group chat: %1$s</string> <string name="create_group_chat_failed">Failed to create group chat: %1$s</string>
<string name="create_group_chat_confirm_title">Create Group Chat</string> <string name="create_group_chat_confirm_title">Create Group Chat</string>
<string name="create_group_chat_required_cost">Required consumption:</string> <string name="create_group_chat_required_cost">Required consumption:</string>