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 0000000..e9cf51c Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/icon_agent_chat_empty.png differ 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 0000000..c4b2ce6 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/icon_friend_chat_empty.png differ diff --git a/app/src/main/res/mipmap-xhdpi/icon_group_chat_empty.png b/app/src/main/res/mipmap-xhdpi/icon_group_chat_empty.png new file mode 100644 index 0000000..9fa076a Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/icon_group_chat_empty.png differ diff --git a/app/src/main/res/mipmap-xhdpi/rider_pro_im_send.png b/app/src/main/res/mipmap-xhdpi/rider_pro_im_send.png new file mode 100644 index 0000000..dd9eaa7 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/rider_pro_im_send.png differ 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 + 没有群聊消息的宇宙太安静了 + 在首页探索感兴趣的主题房间 暂无朋友聊天 开始与朋友对话吧 我: