diff --git a/app/src/main/java/com/aiosman/ravenow/data/PointService.kt b/app/src/main/java/com/aiosman/ravenow/data/PointService.kt index d246288..97718bb 100644 --- a/app/src/main/java/com/aiosman/ravenow/data/PointService.kt +++ b/app/src/main/java/com/aiosman/ravenow/data/PointService.kt @@ -1,6 +1,8 @@ package com.aiosman.ravenow.data +import android.content.Context import com.aiosman.ravenow.AppStore +import com.aiosman.ravenow.R import com.aiosman.ravenow.data.api.ApiClient import com.aiosman.ravenow.data.api.DictItem import com.aiosman.ravenow.data.api.PointsBalance @@ -370,27 +372,29 @@ object PointService { } /** - * 获取变更原因的中文描述 + * 获取变更原因的描述(支持多语言) * + * @param context Context 用于获取资源 * @param reason 变更原因代码 - * @return 中文描述 + * @return 本地化描述 */ - fun getReasonDescription(reason: String): String { - return when (reason) { - ChangeReason.EARN_REGISTER -> "新用户注册奖励" - ChangeReason.EARN_DAILY -> "每日签到奖励" - ChangeReason.EARN_TASK -> "任务完成奖励" - ChangeReason.EARN_INVITE -> "邀请好友奖励" - ChangeReason.EARN_RECHARGE -> "充值获得" - ChangeReason.SPEND_GROUP_CREATE -> "创建群聊" - ChangeReason.SPEND_GROUP_EXPAND -> "扩容群聊" - ChangeReason.SPEND_AGENT_PRIVATE -> "Agent 私密模式" - ChangeReason.SPEND_AGENT_MEMORY -> "Agent 记忆添加" - ChangeReason.SPEND_ROOM_MEMORY -> "房间记忆添加" - ChangeReason.SPEND_CHAT_BACKGROUND -> "自定义聊天背景" - ChangeReason.SPEND_SCHEDULE_EVENT -> "定时事件解锁" - else -> reason // 未知原因,返回原始代码 + fun getReasonDescription(context: Context, reason: String): String { + val resourceId = when (reason) { + ChangeReason.EARN_REGISTER -> R.string.earn_register + ChangeReason.EARN_DAILY -> R.string.earn_daily + ChangeReason.EARN_TASK -> R.string.earn_task + ChangeReason.EARN_INVITE -> R.string.earn_invite + ChangeReason.EARN_RECHARGE -> R.string.earn_recharge + ChangeReason.SPEND_GROUP_CREATE -> R.string.spend_group_create + ChangeReason.SPEND_GROUP_EXPAND -> R.string.spend_group_expand + ChangeReason.SPEND_AGENT_PRIVATE -> R.string.spend_agent_private + ChangeReason.SPEND_AGENT_MEMORY -> R.string.spend_agent_memory + ChangeReason.SPEND_ROOM_MEMORY -> R.string.spend_room_memory + ChangeReason.SPEND_CHAT_BACKGROUND -> R.string.spend_chat_background + ChangeReason.SPEND_SCHEDULE_EVENT -> R.string.spend_schedule_event + else -> null } + return resourceId?.let { context.getString(it) } ?: reason // 未知原因,返回原始代码 } /** diff --git a/app/src/main/java/com/aiosman/ravenow/ui/account/AccountEditViewModel.kt b/app/src/main/java/com/aiosman/ravenow/ui/account/AccountEditViewModel.kt index 18431f7..e0239a2 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/account/AccountEditViewModel.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/account/AccountEditViewModel.kt @@ -31,7 +31,12 @@ object AccountEditViewModel : ViewModel() { // 本地扩展字段 var mbti by mutableStateOf(null) var zodiac by mutableStateOf(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: 开始加载用户资料") isLoading = true try { @@ -41,13 +46,23 @@ object AccountEditViewModel : ViewModel() { profile = it name = it.nickName bio = it.bio - // 清除之前裁剪的图片 - croppedBitmap = null + // 保存原始值,用于取消时恢复 + originalName = it.nickName + originalBio = it.bio + // 只在明确要求时清除之前裁剪的图片(例如保存成功后) + if (clearCroppedBitmap) { + croppedBitmap = null + } // 读取本地扩展字段 try { val uid = it.id // 使用 profile 的 id,确保非空 - mbti = com.aiosman.ravenow.AppStore.getUserMbti(uid) - zodiac = com.aiosman.ravenow.AppStore.getUserZodiac(uid) + val loadedMbti = com.aiosman.ravenow.AppStore.getUserMbti(uid) + val loadedZodiac = com.aiosman.ravenow.AppStore.getUserZodiac(uid) + mbti = loadedMbti + zodiac = loadedZodiac + // 保存原始值 + originalMbti = loadedMbti + originalZodiac = loadedZodiac } catch (_: Exception) { } if (updateTrtcProfile) { TrtcHelper.updateTrtcProfile( @@ -69,12 +84,15 @@ object AccountEditViewModel : ViewModel() { } fun resetToOriginalData() { - profile?.let { - name = it.nickName - bio = it.bio - // 清除之前裁剪的图片 - croppedBitmap = null - } + // 恢复所有字段到原始值 + name = originalName + bio = originalBio + mbti = originalMbti + zodiac = originalZodiac + // 清除之前裁剪的图片和壁纸 + croppedBitmap = null + bannerImageUrl = null + bannerFile = null } @@ -129,8 +147,8 @@ object AccountEditViewModel : ViewModel() { // 清除背景图状态 bannerImageUrl = null bannerFile = null - // 刷新用户资料 - reloadProfile() + // 刷新用户资料,保存成功后清除裁剪的图片 + reloadProfile(clearCroppedBitmap = true) // 刷新个人资料页面的用户资料 MyProfileViewModel.loadUserProfile() } diff --git a/app/src/main/java/com/aiosman/ravenow/ui/account/edit2.kt b/app/src/main/java/com/aiosman/ravenow/ui/account/edit2.kt index 69fc020..b1006ec 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/account/edit2.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/account/edit2.kt @@ -74,6 +74,7 @@ import com.aiosman.ravenow.ConstVars import com.aiosman.ravenow.ui.composables.pickupAndCompressLauncher import android.widget.Toast import java.io.File +import androidx.activity.compose.BackHandler /** * 编辑用户资料界面 @@ -171,6 +172,13 @@ fun AccountEditScreen2(onUpdateBanner: ((Uri, File, Context) -> Unit)? = null,) model.reloadProfile() } + // 处理系统返回键 + BackHandler { + // 用户未保存直接返回,恢复所有字段到原始值 + model.resetToOriginalData() + navController.navigateUp() + } + // 设置状态栏为透明,根据暗色模式决定图标颜色 val systemUiController = rememberSystemUiController() LaunchedEffect(Unit) { @@ -291,6 +299,8 @@ fun AccountEditScreen2(onUpdateBanner: ((Uri, File, Context) -> Unit)? = null,) .clip(CircleShape) .background(Color.White.copy(alpha = 0.3f)) .noRippleClickable { + // 用户未保存直接返回,恢复所有字段到原始值 + model.resetToOriginalData() navController.navigateUp() } .align(Alignment.CenterStart), diff --git a/app/src/main/java/com/aiosman/ravenow/ui/composables/TabItem.kt b/app/src/main/java/com/aiosman/ravenow/ui/composables/TabItem.kt index 4c9ea55..f1dd4c2 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/composables/TabItem.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/composables/TabItem.kt @@ -6,8 +6,10 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size 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.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip 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.sp import com.aiosman.ravenow.LocalAppTheme @@ -74,6 +80,18 @@ fun UnderlineTabItem( modifier: Modifier = Modifier ) { 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( modifier = modifier @@ -81,24 +99,33 @@ fun UnderlineTabItem( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { - Text( - text = text, - fontSize = 15.sp, - fontWeight = FontWeight.ExtraBold, - color = if (isSelected) AppColors.text else AppColors.text.copy(alpha = 0.6f), - modifier = Modifier.padding(horizontal = 16.dp).padding(top = 13.dp) - ) + Box( + modifier = Modifier + .padding(horizontal = animatedPadding) + .padding(top = 13.dp, bottom = 0.dp), + contentAlignment = Alignment.BottomCenter + ) { + 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( - modifier = Modifier.size(24.dp), - contentAlignment = Alignment.Center - ) { - if (isSelected) { + if (isSelected) { + Box( + modifier = Modifier + .size(24.dp) + .offset(y = (-4).dp), + contentAlignment = Alignment.Center + ) { Image( painter = painterResource(id = R.mipmap.underline), contentDescription = "selected indicator", - + modifier = Modifier.fillMaxSize() ) } } diff --git a/app/src/main/java/com/aiosman/ravenow/ui/crop/ImageCropScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/crop/ImageCropScreen.kt index 3677ad2..ab834f0 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/crop/ImageCropScreen.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/crop/ImageCropScreen.kt @@ -72,6 +72,8 @@ fun ImageCropScreen() { } } if (uri == null) { + // 用户取消图片选择,清除已裁剪的图片 + AccountEditViewModel.croppedBitmap = null navController.popBackStack() } } @@ -103,6 +105,8 @@ fun ImageCropScreen() { painter = painterResource(R.drawable.rider_pro_back_icon), contentDescription = null, modifier = Modifier.clickable { + // 用户取消头像选择,清除已裁剪的图片 + AccountEditViewModel.croppedBitmap = null navController.popBackStack() }, colorFilter = ColorFilter.tint(Color.White) @@ -119,11 +123,9 @@ fun ImageCropScreen() { val bitmap = it.onCrop() // 专门处理个人资料头像 + // 只设置裁剪后的图片,不立即上传,等待用户在编辑资料界面点击保存 AccountEditViewModel.croppedBitmap = bitmap - AccountEditViewModel.viewModelScope.launch { - AccountEditViewModel.updateUserProfile(context) - navController.popBackStack() - } + navController.popBackStack() } } ) diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AllChatListScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AllChatListScreen.kt index edb66e9..15e87f7 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AllChatListScreen.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AllChatListScreen.kt @@ -121,35 +121,88 @@ fun AllChatListScreen() { var isLoading by remember { mutableStateOf(false) } var error by remember { mutableStateOf(null) } + // 监听各个 ViewModel 的列表变化 + val agentChatList = AgentChatListViewModel.agentChatList + val groupChatList = GroupChatListViewModel.groupChatList + val friendChatList = FriendChatListViewModel.friendChatList + + // 当列表变化时,自动更新合并列表 + LaunchedEffect(agentChatList, groupChatList, friendChatList) { + val combinedList = mutableListOf() + + 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( refreshing = refreshing, onRefresh = { refreshing = true - refreshAllData(context, - onSuccess = { conversations -> - allConversations = conversations - refreshing = false - }, - onError = { errorMsg -> - error = errorMsg - refreshing = false - } - ) + // 刷新所有类型的数据 + AgentChatListViewModel.refreshPager(pullRefresh = true, context = context) + GroupChatListViewModel.refreshPager(pullRefresh = true, context = context) + FriendChatListViewModel.refreshPager(pullRefresh = true, context = context) } ) + // 监听刷新状态 + LaunchedEffect(AgentChatListViewModel.refreshing, GroupChatListViewModel.refreshing, FriendChatListViewModel.refreshing) { + val isAnyRefreshing = AgentChatListViewModel.refreshing || + GroupChatListViewModel.refreshing || + FriendChatListViewModel.refreshing + if (!isAnyRefreshing && refreshing) { + refreshing = false + } + } + LaunchedEffect(Unit) { isLoading = true - refreshAllData(context, - onSuccess = { conversations -> - allConversations = conversations - isLoading = false - }, - onError = { errorMsg -> - error = errorMsg - isLoading = false - } - ) + // 初始化加载所有类型的数据 + AgentChatListViewModel.refreshPager(context = context) + GroupChatListViewModel.refreshPager(context = context) + FriendChatListViewModel.refreshPager(context = context) } Column( @@ -235,16 +288,10 @@ fun AllChatListScreen() { ReloadButton( onClick = { isLoading = true - refreshAllData(context, - onSuccess = { conversations -> - allConversations = conversations - isLoading = false - }, - onError = { errorMsg -> - error = errorMsg - isLoading = false - } - ) + // 重新加载所有类型的数据 + AgentChatListViewModel.refreshPager(context = context) + GroupChatListViewModel.refreshPager(context = context) + FriendChatListViewModel.refreshPager(context = context) } ) } @@ -363,44 +410,3 @@ fun AllChatListScreen() { } } -fun refreshAllData( - context: android.content.Context, - onSuccess: (List) -> Unit, - onError: (String) -> Unit -) { - try { - // 同时刷新所有类型的数据 - AgentChatListViewModel.refreshPager(context = context) - GroupChatListViewModel.refreshPager(context = context) - FriendChatListViewModel.refreshPager(context = context) - - val combinedList = mutableListOf() - - 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}") - } -} diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/Moment.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/Moment.kt index 7dd5ecd..50f6d7a 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/Moment.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/Moment.kt @@ -104,7 +104,7 @@ fun MomentsList( Row( modifier = Modifier .fillMaxWidth() - .height(44.dp) + .height(54.dp) .padding(horizontal = 16.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/tabs/news/NewsScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/tabs/news/NewsScreen.kt index f7fec19..efa89a3 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/tabs/news/NewsScreen.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/tabs/news/NewsScreen.kt @@ -390,7 +390,8 @@ fun NewsItem( NewsActionButton( icon = if (moment.isFavorite) R.mipmap.icon_variant_2 else R.mipmap.icon_collect, count = moment.favoriteCount.toString(), - isActive = moment.isFavorite, + isActive = false, // 收藏后不使用红色滤镜,保持图标原本颜色 + keepOriginalColor = moment.isFavorite, // 收藏后保持原始颜色 modifier = Modifier.noRippleClickable { onFavoriteClick() } ) @@ -406,6 +407,9 @@ fun NewsItem( } } +// 激活状态的颜色(点赞/收藏时的红色) +private val ActiveIconColor = Color(0xFFD80264) + // 互动栏按钮 @Composable fun NewsActionButton( @@ -414,7 +418,8 @@ fun NewsActionButton( isActive: Boolean, modifier: Modifier = Modifier, 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 @@ -432,7 +437,12 @@ fun NewsActionButton( Image( painter = androidx.compose.ui.res.painterResource(id = icon), 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()) { diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/search/SearchScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/search/SearchScreen.kt index 89f1b3e..27539a5 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/search/SearchScreen.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/search/SearchScreen.kt @@ -69,14 +69,18 @@ import com.aiosman.ravenow.LocalAppTheme import com.aiosman.ravenow.LocalNavController import com.aiosman.ravenow.R 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.MomentCard import com.aiosman.ravenow.ui.composables.TabItem 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.profile.composable.RoomItem import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.aiosman.ravenow.ui.navigateToGroupChat +import com.aiosman.ravenow.ui.NavigationRoute import com.google.accompanist.systemuicontroller.rememberSystemUiController import kotlinx.coroutines.launch import com.aiosman.ravenow.utils.NetworkUtils @@ -403,6 +407,8 @@ fun MomentResultTab() { var moments = dataFlow.collectAsLazyPagingItems() val AppColors = LocalAppTheme.current val context = LocalContext.current + val scope = rememberCoroutineScope() + val navController = LocalNavController.current Box( modifier = Modifier .fillMaxSize() @@ -473,6 +479,12 @@ fun MomentResultTab() { ) { items(moments.itemCount) { idx -> val momentItem = moments[idx] ?: return@items + + val commentDebouncer = rememberDebouncer() + val likeDebouncer = rememberDebouncer() + val favoriteDebouncer = rememberDebouncer() + val followDebouncer = rememberDebouncer() + Box( modifier = Modifier .fillMaxWidth() @@ -480,10 +492,61 @@ fun MomentResultTab() { ) { MomentCard( 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 = { - 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)) @@ -663,6 +726,8 @@ fun AiResultTab() { val context = LocalContext.current val model = SearchViewModel val agents = model.agentsFlow.collectAsLazyPagingItems() + val navController = LocalNavController.current + val scope = rememberCoroutineScope() Box( modifier = Modifier .fillMaxSize() @@ -736,7 +801,23 @@ fun AiResultTab() { Row( modifier = Modifier .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 ) { CustomAsyncImage( diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/search/SearchViewModel.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/search/SearchViewModel.kt index fdb332a..45920e4 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/search/SearchViewModel.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/search/SearchViewModel.kt @@ -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 + } } \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/ravenow/ui/points/PointsBottomSheet.kt b/app/src/main/java/com/aiosman/ravenow/ui/points/PointsBottomSheet.kt index a8744dc..eda311f 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/points/PointsBottomSheet.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/points/PointsBottomSheet.kt @@ -57,6 +57,7 @@ import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp @@ -453,6 +454,7 @@ private fun PointsHistoryList( hasNext: Boolean ) { val AppColors = LocalAppTheme.current + val context = LocalContext.current val numberFormat = remember { NumberFormat.getNumberInstance(Locale.getDefault()) } val listState = rememberLazyListState() val loading = PointsViewModel.loading @@ -544,7 +546,7 @@ private fun PointsHistoryList( ) Spacer(Modifier.size(12.dp)) 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)) Text(text = item.createdAt ?: "", color = AppColors.secondaryText, fontSize = 12.sp) } diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 19f9b0d..4367e5a 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -362,6 +362,21 @@ タスクを完了して報酬を獲得 パイコインをチャージ 複数のパッケージが利用可能、今すぐチャージ + + + 新規ユーザー登録報酬 + デイリーチェックイン報酬 + タスク完了報酬 + 友達招待報酬 + チャージ + グループチャット作成 + グループチャット拡張 + エージェントプライベートモード + エージェント記憶追加 + ルーム記憶追加 + カスタムチャット背景 + スケジュールイベント解除 + グループチャットの作成に失敗しました: %1$s 世界をつなぐ、フォローから始めましょう エージェントから世界を知り始めませんか? diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 5907627..f125150 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -367,6 +367,21 @@ 完成任务可获得奖励 充值派币 多种套餐可选,立即充值 + + + 新用户注册奖励 + 每日签到奖励 + 任务完成奖励 + 邀请好友奖励 + 充值获得 + 创建群聊 + 扩容群聊 + Agent 私密模式 + Agent 记忆添加 + 房间记忆添加 + 自定义聊天背景 + 定时事件解锁 + 创建群聊失败: %1$s 创建群聊确认 需要消耗: diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eadf2ba..86ca961 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -361,6 +361,21 @@ Complete tasks to earn rewards Recharge Pai Coin Multiple packages available, recharge now + + + New User Registration Reward + Daily Check-in Reward + Task Completion Reward + Invite Friends Reward + Recharge + Create Group Chat + Expand Group Chat + Agent Private Mode + Agent Memory Added + Room Memory Added + Custom Chat Background + Scheduled Event Unlocked + Failed to create group chat: %1$s Create Group Chat Required consumption: