From 5ee1897739cbdb00abe1cd0a3413f1983392c4de Mon Sep 17 00:00:00 2001 From: weber Date: Thu, 21 Aug 2025 17:08:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B6=88=E6=81=AF=E5=88=97=E8=A1=A8=E5=92=8C?= =?UTF-8?q?=E8=81=8A=E5=A4=A9=E6=97=B6=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aiosman/ravenow/data/api/RiderProAPI.kt | 9 +- .../java/com/aiosman/ravenow/entity/Group.kt | 16 + .../main/java/com/aiosman/ravenow/ui/Navi.kt | 14 + .../ravenow/ui/chat/GroupChatScreen.kt | 157 +++---- .../ravenow/ui/group/GroupChatInfoScreen.kt | 386 ++++++++++++++++++ .../ui/group/GroupChatInfoViewModel.kt | 60 +++ .../com/aiosman/ravenow/ui/index/Index.kt | 38 ++ .../ui/index/tabs/message/MessageList.kt | 113 ++++- .../tabs/message/tab/AgentChatListScreen.kt | 16 +- .../message/tab/AgentChatListViewModel.kt | 8 +- .../tabs/message/tab/FriendChatListScreen.kt | 13 +- .../message/tab/FriendChatListViewModel.kt | 4 + .../tabs/message/tab/GroupChatListScreen.kt | 22 +- .../message/tab/GroupChatListViewModel.kt | 4 + .../ravenow/ui/index/tabs/moment/Moment.kt | 34 +- .../index/tabs/moment/tabs/expolre/Explore.kt | 185 +++++---- .../mipmap-xhdpi/icon_agent_chat_empty.png | Bin 0 -> 4555 bytes .../mipmap-xhdpi/icon_friend_chat_empty.png | Bin 0 -> 9314 bytes .../mipmap-xhdpi/icon_group_chat_empty.png | Bin 0 -> 7032 bytes .../res/mipmap-xhdpi/rider_pro_im_send.png | Bin 0 -> 3654 bytes app/src/main/res/values-zh/strings.xml | 7 +- app/src/main/res/values/strings.xml | 2 + 22 files changed, 906 insertions(+), 182 deletions(-) create mode 100644 app/src/main/java/com/aiosman/ravenow/entity/Group.kt create mode 100644 app/src/main/java/com/aiosman/ravenow/ui/group/GroupChatInfoScreen.kt create mode 100644 app/src/main/java/com/aiosman/ravenow/ui/group/GroupChatInfoViewModel.kt create mode 100644 app/src/main/res/mipmap-xhdpi/icon_agent_chat_empty.png create mode 100644 app/src/main/res/mipmap-xhdpi/icon_friend_chat_empty.png create mode 100644 app/src/main/res/mipmap-xhdpi/icon_group_chat_empty.png create mode 100644 app/src/main/res/mipmap-xhdpi/rider_pro_im_send.png diff --git a/app/src/main/java/com/aiosman/ravenow/data/api/RiderProAPI.kt b/app/src/main/java/com/aiosman/ravenow/data/api/RiderProAPI.kt index f491fdd..73c516d 100644 --- a/app/src/main/java/com/aiosman/ravenow/data/api/RiderProAPI.kt +++ b/app/src/main/java/com/aiosman/ravenow/data/api/RiderProAPI.kt @@ -41,7 +41,9 @@ data class AgentMomentRequestBody( data class SingleChatRequestBody( @SerializedName("agentOpenId") - val generateText: String, + val generateText: String? = null, + @SerializedName("agentTrtcId") + val agentTrtcId: String? = null, ) data class GroupChatRequestBody( @@ -561,6 +563,11 @@ interface RaveNowAPI { @Query("isRecommended") isRecommended: Int = 1, ): Response> + @GET("rooms/detail") + suspend fun getRoomDetail(@Query("trtcId") trtcId: String, + ): Response> + + } diff --git a/app/src/main/java/com/aiosman/ravenow/entity/Group.kt b/app/src/main/java/com/aiosman/ravenow/entity/Group.kt new file mode 100644 index 0000000..a03c550 --- /dev/null +++ b/app/src/main/java/com/aiosman/ravenow/entity/Group.kt @@ -0,0 +1,16 @@ +package com.aiosman.ravenow.entity + +data class GroupMember( + val userId: String, + val nickname: String, + val avatar: String, + val isOwner: Boolean = false +) + +data class GroupInfo( + val groupId: String, + val groupName: String, + val groupAvatar: String, + val memberCount: Int, + val members: List +) \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/ravenow/ui/Navi.kt b/app/src/main/java/com/aiosman/ravenow/ui/Navi.kt index a93c69a..9a9162c 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/Navi.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/Navi.kt @@ -35,6 +35,7 @@ import com.aiosman.ravenow.ui.agent.AddAgentScreen import com.aiosman.ravenow.ui.group.CreateGroupChatScreen import com.aiosman.ravenow.ui.chat.ChatAiScreen import com.aiosman.ravenow.ui.chat.ChatScreen +import com.aiosman.ravenow.ui.group.GroupChatInfoScreen import com.aiosman.ravenow.ui.chat.GroupChatScreen import com.aiosman.ravenow.ui.comment.CommentsScreen import com.aiosman.ravenow.ui.comment.notice.CommentNoticeScreen @@ -96,6 +97,7 @@ sealed class NavigationRoute( data object Chat : NavigationRoute("Chat/{id}") data object ChatAi : NavigationRoute("ChatAi/{id}") data object ChatGroup : NavigationRoute("ChatGroup/{id}/{name}/{avatar}") + data object GroupChatInfo : NavigationRoute("GroupChatInfo/{groupId}") data object CommentNoticeScreen : NavigationRoute("CommentNoticeScreen") data object ImageCrop : NavigationRoute("ImageCrop") data object AccountSetting : NavigationRoute("AccountSetting") @@ -404,6 +406,18 @@ fun NavigationController( } } + composable( + route = NavigationRoute.GroupChatInfo.route, + arguments = listOf(navArgument("groupId") { type = NavType.StringType }) + ) { + val groupId = it.arguments?.getString("groupId") ?: "" + CompositionLocalProvider( + LocalAnimatedContentScope provides this, + ) { + GroupChatInfoScreen(groupId) + } + } + composable(route = NavigationRoute.CommentNoticeScreen.route) { CompositionLocalProvider( diff --git a/app/src/main/java/com/aiosman/ravenow/ui/chat/GroupChatScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/chat/GroupChatScreen.kt index 8d6b30c..ffea47b 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/chat/GroupChatScreen.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/chat/GroupChatScreen.kt @@ -8,6 +8,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.Crossfade import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -49,6 +50,7 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow import androidx.compose.ui.focus.onFocusChanged @@ -229,7 +231,8 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) { modifier = Modifier .size(28.dp) .noRippleClickable { - isMenuExpanded = true + // 跳转到群聊信息页面 + navController.navigate("GroupChatInfo/$groupId") }, contentDescription = null, colorFilter = ColorFilter.tint(AppColors.text) @@ -276,9 +279,8 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) { modifier = Modifier .fillMaxWidth() .height(1.dp) - .background(AppColors.decentBackground) ) - Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(12.dp)) GroupChatInput( onSendImage = { uri -> uri?.let { @@ -294,7 +296,7 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) { Box( modifier = Modifier .fillMaxSize() - .background(AppColors.decentBackground) + .background(AppColors.background) .padding(paddingValues) ) { LazyColumn( @@ -306,23 +308,31 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) { val chatList = groupMessagesByTime(viewModel.getDisplayChatList(), viewModel) items(chatList.size, key = { index -> chatList[index].msgId + UUID.randomUUID().toString()}) { index -> val item = chatList[index] - if (item.showTimeDivider) { - val calendar = java.util.Calendar.getInstance() - calendar.timeInMillis = item.timestamp - Text( - text = calendar.time.formatChatTime(context), - style = TextStyle( - color = AppColors.secondaryText, - fontSize = 14.sp, - textAlign = TextAlign.Center - ), - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 8.dp) + Column { + if (item.showTimeDivider) { + val calendar = java.util.Calendar.getInstance() + calendar.timeInMillis = item.timestamp + Text( + text = calendar.time.formatChatTime(context), + style = TextStyle( + color = AppColors.secondaryText, + fontSize = 11.sp, + textAlign = TextAlign.Center + ), + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp) + ) + } + // 获取上一个item的userId,用于判断是否显示头像和昵称 + val previousItem = if (index < chatList.size - 1) chatList[index + 1] else null + val showAvatarAndNickname = previousItem?.userId != item.userId + GroupChatItem( + item = item, + currentUserId = viewModel.myProfile?.trtcUserId!!, + showAvatarAndNickname = showAvatarAndNickname ) } - - GroupChatItem(item = item, viewModel.myProfile?.trtcUserId!!) } } @@ -345,7 +355,7 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) { text = "${goToNewCount} 条新消息", style = TextStyle( color = AppColors.text, - fontSize = 16.sp, + fontSize = 12.sp, ), ) } @@ -390,7 +400,7 @@ fun GroupChatSelfItem(item: ChatItem) { vertical = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 8.dp else 0.dp), horizontal = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 16.dp else 0.dp) ) - .padding(bottom = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 3.dp else 0.dp)) + ) { when (item.messageType) { V2TIMMessage.V2TIM_ELEM_TYPE_TEXT -> { @@ -441,7 +451,7 @@ fun GroupChatSelfItem(item: ChatItem) { } @Composable -fun GroupChatOtherItem(item: ChatItem) { +fun GroupChatOtherItem(item: ChatItem, showAvatarAndNickname: Boolean = true) { val AppColors = LocalAppTheme.current Column( @@ -451,30 +461,27 @@ fun GroupChatOtherItem(item: ChatItem) { ) { Row( horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.Bottom, modifier = Modifier.fillMaxWidth() ) { - Box( - modifier = Modifier - .size(24.dp) - .clip(RoundedCornerShape(24.dp)) - ) { - CustomAsyncImage( - imageUrl = item.avatar.replace("storage/avatars/", "/avatar/"), - modifier = Modifier.fillMaxSize(), - contentDescription = "avatar" - ) + if (showAvatarAndNickname) { + Box( + modifier = Modifier + .size(24.dp) + .clip(RoundedCornerShape(24.dp)) + ) { + CustomAsyncImage( + imageUrl = item.avatar.replace("storage/avatars/", "/avatar/"), + modifier = Modifier.fillMaxSize(), + contentDescription = "avatar" + ) + } + Spacer(modifier = Modifier.width(12.dp)) + } else { + // 当不显示头像时,添加左边距以保持消息对齐 + Spacer(modifier = Modifier.width(36.dp)) } - Spacer(modifier = Modifier.width(12.dp)) Column { - Text( - text = item.nickname, - style = TextStyle( - color = AppColors.secondaryText, - fontSize = 12.sp, - ), - modifier = Modifier.padding(bottom = 2.dp) - ) - Box( modifier = Modifier .widthIn( @@ -487,7 +494,6 @@ fun GroupChatOtherItem(item: ChatItem) { vertical = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 8.dp else 0.dp), horizontal = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 16.dp else 0.dp) ) - .padding(bottom = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 3.dp else 0.dp)) ) { when (item.messageType) { V2TIMMessage.V2TIM_ELEM_TYPE_TEXT -> { @@ -520,13 +526,24 @@ fun GroupChatOtherItem(item: ChatItem) { } } } + + if (showAvatarAndNickname) { + Text( + text = item.nickname, + style = TextStyle( + color = AppColors.secondaryText, + fontSize = 12.sp, + ), + modifier = Modifier.padding(top = 2.dp) + ) + } } } } } @Composable -fun GroupChatItem(item: ChatItem, currentUserId: String) { +fun GroupChatItem(item: ChatItem, currentUserId: String, showAvatarAndNickname: Boolean = true) { val isCurrentUser = item.userId == currentUserId // 管理员消息显示特殊布局 @@ -538,7 +555,7 @@ fun GroupChatItem(item: ChatItem, currentUserId: String) { // 根据是否是当前用户显示不同样式 when (item.userId) { currentUserId -> GroupChatSelfItem(item) - else -> GroupChatOtherItem(item) + else -> GroupChatOtherItem(item, showAvatarAndNickname) } } @@ -615,20 +632,21 @@ fun GroupChatInput( onSendImage(uri) } } + Box( modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 16.dp, bottom = 12.dp),){ Row( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 16.dp) - .padding(bottom = inputBarHeight) + .clip(RoundedCornerShape(20.dp)) + .background(appColors.decentBackground) + .padding(start = 16.dp, end = 8.dp, top = 2.dp, bottom = 2.dp), + verticalAlignment = Alignment.CenterVertically, ) { Box( modifier = Modifier .weight(1f) - .clip(RoundedCornerShape(16.dp)) - .background(appColors.background) - .padding(horizontal = 16.dp), - contentAlignment = Alignment.CenterStart, ) { BasicTextField( value = text, @@ -640,6 +658,7 @@ fun GroupChatInput( fontSize = 16.sp ), cursorBrush = SolidColor(appColors.text), + singleLine = true, modifier = Modifier .fillMaxWidth() .padding(vertical = 8.dp) @@ -662,44 +681,32 @@ fun GroupChatInput( ) ) } - Spacer(modifier = Modifier.width(16.dp)) - Icon( - painter = painterResource(id = R.drawable.rider_pro_camera), - contentDescription = "发送图片", - modifier = Modifier - .size(30.dp) - .noRippleClickable { - imagePickUpLauncher.launch( - Intent.createChooser( - Intent(Intent.ACTION_GET_CONTENT).apply { - type = "image/*" - }, - "选择图片" - ) - ) - }, - tint = appColors.chatActionColor - ) + Spacer(modifier = Modifier.width(8.dp)) Crossfade( targetState = text.isNotEmpty(), animationSpec = tween(500), label = "" ) { isNotEmpty -> - Icon( - painter = painterResource(id = R.drawable.rider_pro_video_share), - contentDescription = "发送消息", + val alpha by animateFloatAsState( + targetValue = if (isNotEmpty) 1f else 0.5f, + animationSpec = tween(300) + ) + Image( + painter = painterResource(R.mipmap.rider_pro_im_send), modifier = Modifier - .size(32.dp) + .size(24.dp) + .alpha(alpha) .noRippleClickable { if (text.isNotEmpty()) { onSend(text) text = "" } }, - tint = if (isNotEmpty) appColors.main else appColors.chatActionColor + contentDescription = null, ) } } + } } fun groupMessagesByTime(chatList: List, viewModel: GroupChatViewModel): List { @@ -711,7 +718,7 @@ fun groupMessagesByTime(chatList: List, viewModel: GroupChatViewModel) } val currentMessage = chatList[i] val timeDiff = currentMessage.timestamp - chatList[i - 1].timestamp - if (-timeDiff > 30 * 60 * 1000) { + if (-timeDiff > 10 * 60 * 1000) { viewModel.showTimestampMap[currentMessage.msgId] = true currentMessage.showTimeDivider = true } diff --git a/app/src/main/java/com/aiosman/ravenow/ui/group/GroupChatInfoScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/group/GroupChatInfoScreen.kt new file mode 100644 index 0000000..8f2dc2d --- /dev/null +++ b/app/src/main/java/com/aiosman/ravenow/ui/group/GroupChatInfoScreen.kt @@ -0,0 +1,386 @@ +package com.aiosman.ravenow.ui.group + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewmodel.compose.viewModel +import com.aiosman.ravenow.LocalAppTheme +import com.aiosman.ravenow.LocalNavController +import com.aiosman.ravenow.R +import com.aiosman.ravenow.ui.composables.CustomAsyncImage +import com.aiosman.ravenow.ui.composables.StatusBarSpacer +import com.aiosman.ravenow.ui.modifiers.noRippleClickable + +@Composable +fun GroupChatInfoScreen(groupId: String) { + val navController = LocalNavController.current + val context = LocalContext.current + val AppColors = LocalAppTheme.current + + val viewModel = viewModel( + key = "GroupChatInfoViewModel_$groupId", + factory = object : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + return GroupChatInfoViewModel(groupId) as T + } + } + ) + + Column( + modifier = Modifier + .fillMaxSize() + .background(AppColors.background) + ) { + // 顶部导航栏 + Column( + modifier = Modifier + .fillMaxWidth() + .background(AppColors.background) + ) { + StatusBarSpacer() + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp, horizontal = 16.dp), + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = painterResource(R.drawable.rider_pro_back_icon), + modifier = Modifier + .size(24.dp) + .noRippleClickable { + navController.navigateUp() + }, + contentDescription = null, + colorFilter = ColorFilter.tint(AppColors.text) + ) + Spacer(modifier = Modifier.width(16.dp)) + Text( + modifier = Modifier.fillMaxWidth(), + text = "群聊信息", + style = androidx.compose.ui.text.TextStyle( + color = AppColors.text, + fontSize = 18.sp, + fontWeight = FontWeight.Bold + ) + + ) + Spacer(modifier = Modifier.width(40.dp)) + } + } + + // 内容区域 + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues(16.dp) + ) { + // 群聊头像和名称 + item { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + if (viewModel.groupInfo?.groupAvatar?.isNotEmpty() == true) { + CustomAsyncImage( + imageUrl = viewModel.groupInfo!!.groupAvatar, + modifier = Modifier + .size(80.dp) + .clip(CircleShape), + contentDescription = "群聊头像" + ) + } else { + Box( + modifier = Modifier + .size(80.dp) + .clip(CircleShape) + .background(AppColors.decentBackground), + contentAlignment = Alignment.Center + ) { + Text( + text = viewModel.groupInfo?.groupName?.firstOrNull()?.toString() ?: "群", + style = androidx.compose.ui.text.TextStyle( + color = AppColors.text, + fontSize = 24.sp, + fontWeight = FontWeight.Bold + ) + ) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = viewModel.groupInfo?.groupName ?: "群聊", + style = androidx.compose.ui.text.TextStyle( + color = AppColors.text, + fontSize = 18.sp, + fontWeight = FontWeight.Bold + ) + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = "更改名称和图片", + style = androidx.compose.ui.text.TextStyle( + color = Color(0xFF007AFF), + fontSize = 14.sp + ), + modifier = Modifier.noRippleClickable { + // TODO: 实现更改群聊名称和图片功能 + } + ) + } + } + + // 操作按钮 + item { + Spacer(modifier = Modifier.height(32.dp)) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly + ) { + // 添加其他人 + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.noRippleClickable { + // TODO: 实现添加其他人功能 + } + ) { + Box( + modifier = Modifier + .size(48.dp) + .clip(CircleShape) + .background(Color(0xFF007AFF)), + contentAlignment = Alignment.Center + ) { + Image( + painter = painterResource(R.drawable.rider_pro_back_icon), + modifier = Modifier.size(24.dp), + contentDescription = null, + colorFilter = ColorFilter.tint(Color.White) + ) + } + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "添加其他人", + style = androidx.compose.ui.text.TextStyle( + color = AppColors.text, + fontSize = 12.sp + ) + ) + } + + // 通知设置 + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.noRippleClickable { + // TODO: 实现通知设置功能 + } + ) { + Box( + modifier = Modifier + .size(48.dp) + .clip(CircleShape) + .background(Color(0xFF007AFF)), + contentAlignment = Alignment.Center + ) { + Image( + painter = painterResource(R.drawable.rider_pro_notice_active), + modifier = Modifier.size(24.dp), + contentDescription = null, + colorFilter = ColorFilter.tint(Color.White) + ) + } + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "通知", + style = androidx.compose.ui.text.TextStyle( + color = AppColors.text, + fontSize = 12.sp + ) + ) + } + + // 退出群聊 + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.noRippleClickable { + // TODO: 实现退出群聊功能 + } + ) { + Box( + modifier = Modifier + .size(48.dp) + .clip(CircleShape) + .background(Color(0xFFFF3B30)), + contentAlignment = Alignment.Center + ) { + Image( + painter = painterResource(R.drawable.rider_pro_back_icon), + modifier = Modifier.size(24.dp), + contentDescription = null, + colorFilter = ColorFilter.tint(Color.White) + ) + } + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "退出", + style = androidx.compose.ui.text.TextStyle( + color = AppColors.text, + fontSize = 12.sp + ) + ) + } + } + } + + // 设置选项 + item { + Spacer(modifier = Modifier.height(32.dp)) + + // 设置聊天主题 + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(8.dp)) + .background(AppColors.decentBackground) + .padding(16.dp) + .noRippleClickable { + // TODO: 实现设置聊天主题功能 + }, + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = painterResource(R.drawable.rider_pro_back_icon), + modifier = Modifier.size(24.dp), + contentDescription = null, + colorFilter = ColorFilter.tint(AppColors.text) + ) + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = "设置聊天主题", + style = androidx.compose.ui.text.TextStyle( + color = AppColors.text, + fontSize = 16.sp + ), + modifier = Modifier.weight(1f) + ) + Image( + painter = painterResource(R.drawable.rider_pro_back_icon), + modifier = Modifier.size(16.dp), + contentDescription = null, + colorFilter = ColorFilter.tint(AppColors.secondaryText) + ) + } + + Spacer(modifier = Modifier.height(1.dp)) + + // 群聊成员 + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(8.dp)) + .background(AppColors.decentBackground) + .padding(16.dp) + .noRippleClickable { + // TODO: 实现查看群聊成员功能 + }, + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = painterResource(R.drawable.rider_pro_back_icon), + modifier = Modifier.size(24.dp), + contentDescription = null, + colorFilter = ColorFilter.tint(AppColors.text) + ) + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = "群聊成员 (${viewModel.groupInfo?.memberCount ?: 0})", + style = androidx.compose.ui.text.TextStyle( + color = AppColors.text, + fontSize = 16.sp + ), + modifier = Modifier.weight(1f) + ) + Image( + painter = painterResource(R.drawable.rider_pro_back_icon), + modifier = Modifier.size(16.dp), + contentDescription = null, + colorFilter = ColorFilter.tint(AppColors.secondaryText) + ) + } + } + + // 成员列表 + if (viewModel.groupInfo?.members?.isNotEmpty() == true) { + item { + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = "群成员", + style = androidx.compose.ui.text.TextStyle( + color = AppColors.text, + fontSize = 16.sp, + fontWeight = FontWeight.Bold + ) + ) + } + + items(viewModel.groupInfo!!.members) { member -> + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + CustomAsyncImage( + imageUrl = member.avatar, + modifier = Modifier + .size(40.dp) + .clip(CircleShape), + contentDescription = "成员头像" + ) + Spacer(modifier = Modifier.width(12.dp)) + Column { + Text( + text = member.nickname, + style = androidx.compose.ui.text.TextStyle( + color = AppColors.text, + fontSize = 16.sp + ) + ) + if (member.isOwner) { + Text( + text = "群主", + style = androidx.compose.ui.text.TextStyle( + color = AppColors.secondaryText, + fontSize = 12.sp + ) + ) + } + } + } + } + } + } + } +} diff --git a/app/src/main/java/com/aiosman/ravenow/ui/group/GroupChatInfoViewModel.kt b/app/src/main/java/com/aiosman/ravenow/ui/group/GroupChatInfoViewModel.kt new file mode 100644 index 0000000..e28d0ec --- /dev/null +++ b/app/src/main/java/com/aiosman/ravenow/ui/group/GroupChatInfoViewModel.kt @@ -0,0 +1,60 @@ +package com.aiosman.ravenow.ui.group + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.aiosman.ravenow.data.api.ApiClient +import com.aiosman.ravenow.entity.GroupInfo +import com.aiosman.ravenow.entity.GroupMember +import kotlinx.coroutines.launch + +class GroupChatInfoViewModel( + private val groupId: String +) : ViewModel() { + + var groupInfo by mutableStateOf(null) + var isLoading by mutableStateOf(false) + var error by mutableStateOf(null) + + init { + loadGroupInfo() + } + + private fun loadGroupInfo() { + viewModelScope.launch { + try { + isLoading = true + error = null + + // 调用接口获取群聊详情 + val response = ApiClient.api.getRoomDetail(trtcId = groupId) + + // 使用接口返回的数据 + val room = response.body()?.data + groupInfo = room?.let { + GroupInfo( + groupId = groupId, + groupName = it.name, + groupAvatar = room.avatar, + memberCount = room.userCount, + members = listOf( + GroupMember( + userId = room.creator.userId, + nickname = room.creator.profile.nickname, + avatar = room.creator.profile.avatar, + isOwner = true + ) + ) + ) + } + + } catch (e: Exception) { + error = e.message ?: "加载失败" + } finally { + isLoading = false + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt index 15df560..a5c65dd 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt @@ -21,8 +21,10 @@ import androidx.compose.foundation.layout.requiredWidth import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.DrawerValue import androidx.compose.material3.Icon @@ -55,6 +57,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.offset import androidx.compose.ui.unit.sp import com.aiosman.ravenow.AppState import com.aiosman.ravenow.AppStore @@ -66,6 +69,9 @@ import com.aiosman.ravenow.ui.NavigationRoute import com.aiosman.ravenow.ui.index.tabs.add.AddPage import com.aiosman.ravenow.ui.index.tabs.ai.Agent import com.aiosman.ravenow.ui.index.tabs.message.NotificationsScreen +import com.aiosman.ravenow.ui.index.tabs.message.tab.AgentChatListViewModel +import com.aiosman.ravenow.ui.index.tabs.message.tab.FriendChatListViewModel +import com.aiosman.ravenow.ui.index.tabs.message.tab.GroupChatListViewModel import com.aiosman.ravenow.ui.index.tabs.moment.MomentsList import com.aiosman.ravenow.ui.index.tabs.profile.ProfileWrap import com.aiosman.ravenow.ui.index.tabs.search.DiscoverScreen @@ -99,6 +105,10 @@ fun IndexScreen() { val context = LocalContext.current LaunchedEffect(Unit) { systemUiController.setNavigationBarColor(Color.Transparent) + // 初始化聊天列表以获取未读消息数 + AgentChatListViewModel.refreshPager(context = context) + GroupChatListViewModel.refreshPager(context = context) + FriendChatListViewModel.refreshPager(context = context) } LaunchedEffect(model.openDrawer) { if (model.openDrawer) { @@ -311,12 +321,40 @@ fun IndexScreen() { ), contentAlignment = Alignment.Center ) { + Box( + modifier = Modifier + .width(24.dp) + .height(24.dp) + , + contentAlignment = Alignment.Center + ) { Image( painter = painterResource(if (isSelected) it.selectedIcon() else it.icon()), contentDescription = it.label(), modifier = Modifier.size(24.dp), colorFilter = if (!isSelected) ColorFilter.tint(AppColors.text) else null ) + + // 消息按钮红点显示在图片右上角 + if (it.route == NavigationItem.Notification.route) { + val totalUnreadCount = + AgentChatListViewModel.totalUnreadCount + + GroupChatListViewModel.totalUnreadCount + + FriendChatListViewModel.totalUnreadCount + if (totalUnreadCount > 0) { + Box( + modifier = Modifier + .size(8.dp) + .background( + color = Color(0xFFFF3B30), + shape = CircleShape + ) + .align(Alignment.TopEnd) + .offset(x = 8.dp, y = 8.dp) + ) + } + } + } } // 文字标签,可控制间距 diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/MessageList.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/MessageList.kt index f99885c..1664ac1 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/MessageList.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/MessageList.kt @@ -16,6 +16,8 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.offset + import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBars @@ -50,6 +52,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.compose.ui.unit.offset import androidx.lifecycle.viewModelScope import com.aiosman.ravenow.AppState import com.aiosman.ravenow.AppStore @@ -63,8 +66,11 @@ import com.aiosman.ravenow.ui.composables.TabItem import com.aiosman.ravenow.ui.composables.TabSpacer import com.aiosman.ravenow.ui.follower.FollowerNoticeViewModel import com.aiosman.ravenow.ui.index.tabs.message.tab.AgentChatListScreen +import com.aiosman.ravenow.ui.index.tabs.message.tab.AgentChatListViewModel import com.aiosman.ravenow.ui.index.tabs.message.tab.FriendChatListScreen +import com.aiosman.ravenow.ui.index.tabs.message.tab.FriendChatListViewModel import com.aiosman.ravenow.ui.index.tabs.message.tab.GroupChatListScreen +import com.aiosman.ravenow.ui.index.tabs.message.tab.GroupChatListViewModel import com.aiosman.ravenow.ui.like.LikeNoticeViewModel import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.google.accompanist.systemuicontroller.rememberSystemUiController @@ -77,6 +83,10 @@ import kotlinx.coroutines.launch @OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class) @Composable fun NotificationsScreen() { + // 计算总未读消息数 + val totalUnreadCount = AgentChatListViewModel.totalUnreadCount + + GroupChatListViewModel.totalUnreadCount + + FriendChatListViewModel.totalUnreadCount val AppColors = LocalAppTheme.current val navController = LocalNavController.current val systemUiController = rememberSystemUiController() @@ -87,6 +97,12 @@ fun NotificationsScreen() { MessageListViewModel.viewModelScope.launch { MessageListViewModel.initData(context, force = true, loadChat = AppState.enableChat) } + // 刷新群聊列表以更新未读消息数 + GroupChatListViewModel.refreshPager(context = context) + // 刷新智能体列表以更新未读消息数 + AgentChatListViewModel.refreshPager(context = context) + // 刷新朋友列表以更新未读消息数 + FriendChatListViewModel.refreshPager(context = context) }) val navigationBarPaddings = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp @@ -94,6 +110,12 @@ fun NotificationsScreen() { LaunchedEffect(Unit) { systemUiController.setNavigationBarColor(Color.Transparent) MessageListViewModel.initData(context, loadChat = AppState.enableChat) + // 初始化群聊列表以获取未读消息数 + GroupChatListViewModel.refreshPager(context = context) + // 初始化智能体列表以获取未读消息数 + AgentChatListViewModel.refreshPager(context = context) + // 初始化朋友列表以获取未读消息数 + FriendChatListViewModel.refreshPager(context = context) } Column( modifier = Modifier @@ -202,36 +224,83 @@ fun NotificationsScreen() { horizontalArrangement = Arrangement.Start, verticalAlignment = Alignment.Bottom ) { - TabItem( - text = stringResource(R.string.chat_ai), - isSelected = pagerState.currentPage == 0, - onClick = { - scope.launch { - pagerState.animateScrollToPage(0) + Box { + TabItem( + text = stringResource(R.string.chat_ai), + isSelected = pagerState.currentPage == 0, + onClick = { + scope.launch { + pagerState.animateScrollToPage(0) + } } - + ) + + // 智能体未读消息红点 + if (AgentChatListViewModel.totalUnreadCount > 0) { + Box( + modifier = Modifier + .size(8.dp) + .background( + color = Color(0xFFFF3B30), + shape = CircleShape + ) + .align(Alignment.TopEnd) + .offset(x = 8.dp, y = (-4).dp) + ) } - ) + } TabSpacer() - TabItem( - text = stringResource(R.string.chat_group), - isSelected = pagerState.currentPage == 1, - onClick = { - scope.launch { - pagerState.animateScrollToPage(1) + Box { + TabItem( + text = stringResource(R.string.chat_group), + isSelected = pagerState.currentPage == 1, + onClick = { + scope.launch { + pagerState.animateScrollToPage(1) + } } + ) + + // 群聊未读消息红点 + if (GroupChatListViewModel.totalUnreadCount > 0) { + Box( + modifier = Modifier + .size(8.dp) + .background( + color = Color(0xFFFF3B30), + shape = CircleShape + ) + .align(Alignment.TopEnd) + .offset(x = 8.dp, y = (-4).dp) + ) } - ) + } TabSpacer() - TabItem( - text = stringResource(R.string.chat_friend), - isSelected = pagerState.currentPage == 2, - onClick = { - scope.launch { - pagerState.animateScrollToPage(2) + Box { + TabItem( + text = stringResource(R.string.chat_friend), + isSelected = pagerState.currentPage == 2, + onClick = { + scope.launch { + pagerState.animateScrollToPage(2) + } } + ) + + // 朋友未读消息红点 + if (FriendChatListViewModel.totalUnreadCount > 0) { + Box( + modifier = Modifier + .size(8.dp) + .background( + color = Color(0xFFFF3B30), + shape = CircleShape + ) + .align(Alignment.TopEnd) + .offset(x = 8.dp, y = (-4).dp) + ) } - ) + } } HorizontalPager( state = pagerState, diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AgentChatListScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AgentChatListScreen.kt index bc79e08..741d5f5 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AgentChatListScreen.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AgentChatListScreen.kt @@ -1,5 +1,6 @@ package com.aiosman.ravenow.ui.index.tabs.message.tab +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -29,7 +30,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -38,6 +41,7 @@ import androidx.compose.ui.res.stringResource import com.aiosman.ravenow.LocalAppTheme import com.aiosman.ravenow.LocalNavController import com.aiosman.ravenow.R +import com.aiosman.ravenow.ui.NavigationRoute import com.aiosman.ravenow.ui.composables.CustomAsyncImage import com.aiosman.ravenow.ui.modifiers.noRippleClickable @@ -82,8 +86,16 @@ fun AgentChatListScreen() { .fillMaxSize() .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center + ) { + Spacer(modifier = Modifier.height(80.dp)) + Image( + painter = painterResource(id = R.mipmap.icon_agent_chat_empty), + contentDescription = "null data", + modifier = Modifier + .size(140.dp) + ) + Spacer(modifier = Modifier.height(24.dp)) Text( text = stringResource(R.string.agent_chat_empty_title), color = AppColors.text, @@ -93,7 +105,7 @@ fun AgentChatListScreen() { Spacer(modifier = Modifier.height(8.dp)) Text( text = stringResource(R.string.agent_chat_empty_subtitle), - color = AppColors.secondaryText, + color = AppColors.text, fontSize = 14.sp ) } diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AgentChatListViewModel.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AgentChatListViewModel.kt index e23e344..3bde01e 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AgentChatListViewModel.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AgentChatListViewModel.kt @@ -92,6 +92,10 @@ object AgentChatListViewModel : ViewModel() { var currentPage by mutableStateOf(1) var error by mutableStateOf(null) + // 计算智能体总未读消息数 + val totalUnreadCount: Int + get() = agentChatList.sumOf { it.unreadCount } + private val pageSize = 20 fun refreshPager(pullRefresh: Boolean = false, context: Context? = null) { @@ -200,10 +204,10 @@ object AgentChatListViewModel : ViewModel() { } } fun createSingleChat( - openId: String, + trtcId: String, ) { viewModelScope.launch { - val response = ApiClient.api.createSingleChat(SingleChatRequestBody(generateText = openId)) + val response = ApiClient.api.createSingleChat(SingleChatRequestBody(agentTrtcId = trtcId)) } } diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/FriendChatListScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/FriendChatListScreen.kt index 7a74e5a..b05c113 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/FriendChatListScreen.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/FriendChatListScreen.kt @@ -1,5 +1,6 @@ package com.aiosman.ravenow.ui.index.tabs.message.tab +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -20,6 +21,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow @@ -68,6 +70,15 @@ fun FriendChatListScreen() { horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { + + Spacer(modifier = Modifier.height(80.dp)) + Image( + painter = painterResource(id = R.mipmap.icon_friend_chat_empty), + contentDescription = "null data", + modifier = Modifier + .size(140.dp) + ) + Spacer(modifier = Modifier.height(24.dp)) Text( text = stringResource(R.string.friend_chat_empty_title), color = AppColors.text, @@ -77,7 +88,7 @@ fun FriendChatListScreen() { Spacer(modifier = Modifier.height(8.dp)) Text( text = stringResource(R.string.friend_chat_empty_subtitle), - color = AppColors.secondaryText, + color = AppColors.text, fontSize = 14.sp ) } diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/FriendChatListViewModel.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/FriendChatListViewModel.kt index 2fb2605..1ea4c23 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/FriendChatListViewModel.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/FriendChatListViewModel.kt @@ -86,6 +86,10 @@ object FriendChatListViewModel : ViewModel() { var currentPage by mutableStateOf(1) var error by mutableStateOf(null) + // 计算朋友总未读消息数 + val totalUnreadCount: Int + get() = friendChatList.sumOf { it.unreadCount } + private val pageSize = 20 fun refreshPager(pullRefresh: Boolean = false, context: Context? = null) { diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/GroupChatListScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/GroupChatListScreen.kt index f2f8958..e4aa96d 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/GroupChatListScreen.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/GroupChatListScreen.kt @@ -1,6 +1,8 @@ package com.aiosman.ravenow.ui.index.tabs.message.tab +import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed @@ -21,6 +23,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow @@ -78,16 +81,29 @@ fun GroupChatListScreen() { horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { + + Spacer(modifier = Modifier.height(80.dp)) + Image( + painter = painterResource(id = if (isSystemInDarkTheme()) { + R.mipmap.icon_group_chat_empty // 深色模式图片 + } else { + R.mipmap.icon_group_chat_empty // 浅色模式图片 + }), + contentDescription = "null data", + modifier = Modifier + .size(140.dp) + ) + Spacer(modifier = Modifier.height(24.dp)) Text( - text = "暂无群聊", + text = stringResource(R.string.group_chat_empty_title), color = AppColors.text, fontSize = 16.sp, fontWeight = FontWeight.W600 ) Spacer(modifier = Modifier.height(8.dp)) Text( - text = "您还没有加入任何群聊", - color = AppColors.secondaryText, + text = stringResource(R.string.group_chat_empty_subtitle), + color = AppColors.text, fontSize = 14.sp ) } diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/GroupChatListViewModel.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/GroupChatListViewModel.kt index 9c48176..1be3e8b 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/GroupChatListViewModel.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/GroupChatListViewModel.kt @@ -103,6 +103,10 @@ object GroupChatListViewModel : ViewModel() { var currentPage by mutableStateOf(1) var error by mutableStateOf(null) + // 计算群聊总未读消息数 + val totalUnreadCount: Int + get() = groupChatList.sumOf { it.unreadCount } + private val pageSize = 20 // 消息监听器 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 81e3d01..90ffb08 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 @@ -79,7 +79,7 @@ fun MomentsList() { // center the tabs horizontalArrangement = Arrangement.Start, verticalAlignment = Alignment.CenterVertically - ) {//探索tab + ) { Column( modifier = Modifier .noRippleClickable { @@ -108,8 +108,38 @@ fun MomentsList() { .width(34.dp) .height(4.dp) ) + + } + Spacer(modifier = Modifier.width(16.dp)) + Column( + modifier = Modifier + .noRippleClickable { + scope.launch { + pagerState.animateScrollToPage(1) + } + }, + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = stringResource(R.string.index_dynamic), + fontSize = 16.sp, + color = if (pagerState.currentPage == 1) AppColors.text else AppColors.nonActiveText, + fontWeight = FontWeight.W600) + Spacer(modifier = Modifier.height(4.dp)) + + Image( + painter = painterResource( + if (pagerState.currentPage == 1) R.mipmap.tab_indicator_selected + else R.drawable.tab_indicator_unselected + ), + contentDescription = "tab indicator", + modifier = Modifier + .width(34.dp) + .height(4.dp) + ) + } - //“关注”tab Spacer(modifier = Modifier.width(16.dp)) Column( modifier = Modifier diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/tabs/expolre/Explore.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/tabs/expolre/Explore.kt index c45b472..b012a4c 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/tabs/expolre/Explore.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/tabs/expolre/Explore.kt @@ -48,6 +48,7 @@ import androidx.compose.ui.unit.sp import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.draw.blur +import androidx.compose.ui.graphics.graphicsLayer import com.aiosman.ravenow.ui.composables.CustomAsyncImage import androidx.lifecycle.viewmodel.compose.viewModel import com.aiosman.ravenow.AppStore @@ -232,9 +233,9 @@ fun Explore() { } @Composable - fun AgentPage(agentItems: List, page: Int) { + fun AgentPage(agentItems: List, page: Int, modifier: Modifier = Modifier) { Column( - modifier = Modifier + modifier = modifier .fillMaxSize() .padding(horizontal = 0.dp) ) { @@ -268,11 +269,27 @@ fun Explore() { ) { HorizontalPager( state = pagerState, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), + contentPadding = androidx.compose.foundation.layout.PaddingValues(horizontal = 4.dp), + pageSpacing = 0.dp ) { page -> + // 计算当前页面的偏移量 + val pageOffset = ( + (pagerState.currentPage - page) + pagerState + .currentPageOffsetFraction + ).coerceIn(-1f, 1f) + + // 根据偏移量计算缩放比例 + val scale = 1f - (0.1f * kotlin.math.abs(pageOffset)) + AgentPage( agentItems = agentItems.drop(page * itemsPerPage).take(itemsPerPage), - page = page + page = page, + modifier = Modifier + .graphicsLayer { + scaleX = scale + scaleY = scale + } ) } } @@ -410,12 +427,12 @@ fun Explore() { @Composable - fun BannerCard(bannerItem: BannerItem) { + fun BannerCard(bannerItem: BannerItem, modifier: Modifier = Modifier) { val AppColors = LocalAppTheme.current val context = LocalContext.current Card( - modifier = Modifier + modifier = modifier .fillMaxSize() .padding(horizontal = 0.dp), shape = RoundedCornerShape(20.dp), @@ -595,15 +612,15 @@ fun Explore() { ) { - // 可以添加更多不同高度的内容项 + // 第一块区域 item { Row( modifier = Modifier .fillMaxWidth() .padding(vertical = 6.dp), - horizontalArrangement = androidx.compose.foundation.layout.Arrangement.SpaceEvenly + horizontalArrangement = androidx.compose.foundation.layout.Arrangement.SpaceBetween ) { - // 第一个 + // 第一个 - 靠左显示 Column( modifier = Modifier .clickable { @@ -633,69 +650,75 @@ fun Explore() { ) } - // 第二个 - Column( - modifier = Modifier - .clickable { - navController.navigate( - NavigationRoute.AddAgent.route) - }, - horizontalAlignment = Alignment.CenterHorizontally + // 中间两个 - 平均分布 + Row( + modifier = Modifier.weight(1f), + horizontalArrangement = androidx.compose.foundation.layout.Arrangement.SpaceEvenly ) { - Box( + // 第二个 + Column( modifier = Modifier - .size(64.dp) - .background(Color(0xFF94f9f2), RoundedCornerShape(24.dp)), - contentAlignment = Alignment.Center + .clickable { + navController.navigate( + NavigationRoute.AddAgent.route) + }, + horizontalAlignment = Alignment.CenterHorizontally ) { - Image( - painter = painterResource(R.mipmap.rider_pro_agent), - contentDescription = "创建智能体", - modifier = Modifier.size(24.dp), + Box( + modifier = Modifier + .size(64.dp) + .background(Color(0xFF94f9f2), RoundedCornerShape(24.dp)), + contentAlignment = Alignment.Center + ) { + Image( + painter = painterResource(R.mipmap.rider_pro_agent), + contentDescription = "创建智能体", + modifier = Modifier.size(24.dp), - ) + ) + } + Spacer(modifier = Modifier.size(8.dp)) + Text( + text = "创建Agent", + fontSize = 12.sp, + color = AppColors.text, + fontWeight = androidx.compose.ui.text.font.FontWeight.W500 + ) + } + + // 第三个 + Column( + modifier = Modifier + .clickable { + NewPostViewModel.asNewPost() + navController.navigate("NewPost") + }, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Box( + modifier = Modifier + .size(64.dp) + .background(Color(0xFFfafd5d), RoundedCornerShape(24.dp)), + contentAlignment = Alignment.Center + ) { + Image( + painter = painterResource(R.mipmap.rider_pro_release), + contentDescription = "发布动态", + modifier = Modifier.size(24.dp), + + ) + } + Spacer(modifier = Modifier.size(8.dp)) + Text( + text = "发布动态", + fontSize = 12.sp, + color = AppColors.text, + fontWeight = androidx.compose.ui.text.font.FontWeight.W500 + ) } - Spacer(modifier = Modifier.size(8.dp)) - Text( - text = "创建Agent", - fontSize = 12.sp, - color = AppColors.text, - fontWeight = androidx.compose.ui.text.font.FontWeight.W500 - ) } - // 第三个 - Column( - modifier = Modifier - .clickable { - NewPostViewModel.asNewPost() - navController.navigate("NewPost") - }, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Box( - modifier = Modifier - .size(64.dp) - .background(Color(0xFFfafd5d), RoundedCornerShape(24.dp)), - contentAlignment = Alignment.Center - ) { - Image( - painter = painterResource(R.mipmap.rider_pro_release), - contentDescription = "发布动态", - modifier = Modifier.size(24.dp), - - ) - } - Spacer(modifier = Modifier.size(8.dp)) - Text( - text = "发布动态", - fontSize = 12.sp, - color = AppColors.text, - fontWeight = androidx.compose.ui.text.font.FontWeight.W500 - ) - } - - // 第四个 + // 第四个 - 靠右显示 Column( horizontalAlignment = Alignment.CenterHorizontally ) { @@ -767,10 +790,28 @@ fun Explore() { ) { HorizontalPager( state = pagerState, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), + contentPadding = androidx.compose.foundation.layout.PaddingValues(horizontal = 4.dp), ) { page -> val bannerItem = bannerItems[page] - BannerCard(bannerItem = bannerItem) + + // 计算当前页面的偏移量 + val pageOffset = ( + (pagerState.currentPage - page) + pagerState + .currentPageOffsetFraction + ).coerceIn(-1f, 1f) + + // 根据偏移量计算缩放比例 + val scale = 1f - (0.1f * kotlin.math.abs(pageOffset)) + + BannerCard( + bannerItem = bannerItem, + modifier = Modifier + .graphicsLayer { + scaleX = scale + scaleY = scale + } + ) } } @@ -807,7 +848,7 @@ fun Explore() { // 标题 Row( verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(bottom = 30.dp) + modifier = Modifier.padding(bottom = 12.dp) ) { Image( painter = painterResource(R.mipmap.rider_pro_fire2), @@ -848,9 +889,9 @@ fun Explore() { ) Spacer(modifier = Modifier.width(4.dp)) Text( - text = "推荐给你的Agent", - fontSize = 20.sp, - fontWeight = androidx.compose.ui.text.font.FontWeight.W900, + text = "推荐给你的智能体", + fontSize = 16.sp, + fontWeight = androidx.compose.ui.text.font.FontWeight.W600, color = AppColors.text ) } @@ -875,7 +916,7 @@ fun Explore() { Image( painter = painterResource(R.mipmap.rider_pro_hot_room), contentDescription = "chat room", - modifier = Modifier.size(28.dp), + modifier = Modifier.size(24.dp), ) Spacer(modifier = Modifier.width(4.dp)) Text( diff --git a/app/src/main/res/mipmap-xhdpi/icon_agent_chat_empty.png b/app/src/main/res/mipmap-xhdpi/icon_agent_chat_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..e9cf51c08d481a34ae00db458e45847d0589c487 GIT binary patch literal 4555 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 v88iwx^8o2+>T6V}AtU|=(}#>C literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/icon_friend_chat_empty.png b/app/src/main/res/mipmap-xhdpi/icon_friend_chat_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..c4b2ce63d5a9bde35d2387f16058083678e07f5b GIT binary patch literal 9314 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 Y0MN+mEP&Wk%K!iX07*qoM6N<$g7ua^;{X5v literal 0 HcmV?d00001 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index d1683e9..b345a1c 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -147,8 +147,8 @@ 朋友 智能体聊天 - 暂无智能体聊天 - 开始与智能体对话吧 + AI 们在等你开启第一句对话 + 去首页探索一下,主动发起一场对话! 我: [图片] [语音] @@ -159,6 +159,9 @@ 加载更多失败 获取用户信息失败: %s + 没有群聊消息的宇宙太安静了 + 在首页探索感兴趣的主题房间 + 暂无朋友聊天 开始与朋友对话吧 我: diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 410cc06..d66cf46 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -155,6 +155,8 @@ 加载失败 加载更多失败 获取用户信息失败: %s + 没有群聊消息的宇宙太安静了 + 在首页探索感兴趣的主题房间 暂无朋友聊天 开始与朋友对话吧 我: