From 9d3d13a22df65493fa775f32cdd8b75aed443050 Mon Sep 17 00:00:00 2001 From: zhong <2724770085@qq.com> Date: Tue, 14 Oct 2025 17:40:25 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=B6=88=E6=81=AF=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=E2=80=9C=E5=85=A8=E9=83=A8=E2=80=9D=E6=A0=87=E7=AD=BE?= =?UTF-8?q?=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/index/tabs/message/MessageList.kt | 54 ++- .../tabs/message/tab/AllChatListScreen.kt | 390 ++++++++++++++++++ .../profile/composable/SelfProfileAction.kt | 28 +- app/src/main/res/values-ja/strings.xml | 1 + app/src/main/res/values-zh/strings.xml | 3 +- app/src/main/res/values/strings.xml | 1 + 6 files changed, 463 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AllChatListScreen.kt 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 1fb3612..687fe10 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 @@ -78,7 +78,7 @@ import com.aiosman.ravenow.ui.like.LikeNoticeViewModel import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.google.accompanist.systemuicontroller.rememberSystemUiController import kotlinx.coroutines.launch - +import com.aiosman.ravenow.ui.index.tabs.message.tab.AllChatListScreen /** * 消息列表界面 @@ -95,7 +95,7 @@ fun NotificationsScreen() { val navController = LocalNavController.current val systemUiController = rememberSystemUiController() val context = LocalContext.current - var pagerState = rememberPagerState (pageCount = { 3 }) + var pagerState = rememberPagerState (pageCount = { 4 }) var scope = rememberCoroutineScope() val state = rememberPullRefreshState(MessageListViewModel.isLoading, onRefresh = { MessageListViewModel.viewModelScope.launch { @@ -324,7 +324,7 @@ fun NotificationsScreen() { Box { TabItem( - text = stringResource(R.string.chat_ai), + text = stringResource(R.string.chat_all), isSelected = pagerState.currentPage == 0, onClick = { tabDebouncer { @@ -335,6 +335,38 @@ fun NotificationsScreen() { } ) + // 全部未读消息红点 + 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 = (-4).dp) + ) + } + } + TabSpacer() + + Box { + TabItem( + text = stringResource(R.string.chat_ai), + isSelected = pagerState.currentPage == 1, + onClick = { + tabDebouncer { + scope.launch { + pagerState.animateScrollToPage(1) + } + } + } + ) + // 智能体未读消息红点 if (AgentChatListViewModel.totalUnreadCount > 0) { Box( @@ -353,11 +385,11 @@ fun NotificationsScreen() { Box { TabItem( text = stringResource(R.string.chat_group), - isSelected = pagerState.currentPage == 1, + isSelected = pagerState.currentPage == 2, onClick = { tabDebouncer { scope.launch { - pagerState.animateScrollToPage(1) + pagerState.animateScrollToPage(2) } } } @@ -378,14 +410,15 @@ fun NotificationsScreen() { } } TabSpacer() + Box { TabItem( text = stringResource(R.string.chat_friend), - isSelected = pagerState.currentPage == 2, + isSelected = pagerState.currentPage == 3, onClick = { tabDebouncer { scope.launch { - pagerState.animateScrollToPage(2) + pagerState.animateScrollToPage(3) } } } @@ -414,14 +447,17 @@ fun NotificationsScreen() { ) { when (it) { 0 -> { + AllChatListScreen() + } + 1 -> { AgentChatListScreen() } - 1 -> { + 2 -> { GroupChatListScreen() } - 2 -> { + 3 -> { FriendChatListScreen() } } diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AllChatListScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AllChatListScreen.kt new file mode 100644 index 0000000..a7a04fe --- /dev/null +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AllChatListScreen.kt @@ -0,0 +1,390 @@ +package com.aiosman.ravenow.ui.index.tabs.message.tab + +import android.widget.Toast +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.itemsIndexed +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.pullrefresh.PullRefreshIndicator +import androidx.compose.material.pullrefresh.pullRefresh +import androidx.compose.material.pullrefresh.rememberPullRefreshState +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush +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.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.aiosman.ravenow.AppState +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.rememberDebouncer +import com.aiosman.ravenow.ui.modifiers.noRippleClickable +import com.aiosman.ravenow.utils.NetworkUtils +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.ui.text.font.FontFamily + +data class CombinedConversation( + val type: String, // "agent", "group", or "friend" + val agentConversation: AgentConversation? = null, + val groupConversation: GroupConversation? = null, + val friendConversation: FriendConversation? = null +) { + val id: String + get() = when (type) { + "agent" -> "agent_${agentConversation?.id ?: 0}" + "group" -> "group_${groupConversation?.id ?: 0}" + "friend" -> "friend_${friendConversation?.id ?: 0}" + else -> "" + } + + val avatar: String + get() = when (type) { + "agent" -> agentConversation?.avatar ?: "" + "group" -> groupConversation?.avatar ?: "" + "friend" -> friendConversation?.avatar ?: "" + else -> "" + } + + val name: String + get() = when (type) { + "agent" -> agentConversation?.nickname ?: "" + "group" -> groupConversation?.groupName ?: "" + "friend" -> friendConversation?.nickname ?: "" + else -> "" + } + + val lastMessageTime: String + get() = when (type) { + "agent" -> agentConversation?.lastMessageTime ?: "" + "group" -> groupConversation?.lastMessageTime ?: "" + "friend" -> friendConversation?.lastMessageTime ?: "" + else -> "" + } + + val displayText: String + get() = when (type) { + "agent" -> agentConversation?.displayText ?: "" + "group" -> groupConversation?.displayText ?: "" + "friend" -> friendConversation?.displayText ?: "" + else -> "" + } + + val unreadCount: Int + get() = when (type) { + "agent" -> agentConversation?.unreadCount ?: 0 + "group" -> groupConversation?.unreadCount ?: 0 + "friend" -> friendConversation?.unreadCount ?: 0 + else -> 0 + } + + val isSelf: Boolean + get() = when (type) { + "agent" -> agentConversation?.isSelf ?: false + "group" -> groupConversation?.isSelf ?: false + "friend" -> friendConversation?.isSelf ?: false + else -> false + } +} + +@OptIn(ExperimentalMaterialApi::class) +@Composable +fun AllChatListScreen() { + val context = LocalContext.current + val navController = LocalNavController.current + val AppColors = LocalAppTheme.current + + var allConversations by remember { mutableStateOf>(emptyList()) } + var refreshing by remember { mutableStateOf(false) } + var isLoading by remember { mutableStateOf(false) } + var error by remember { mutableStateOf(null) } + + val state = rememberPullRefreshState( + refreshing = refreshing, + onRefresh = { + refreshing = true + refreshAllData(context, + onSuccess = { conversations -> + allConversations = conversations + refreshing = false + }, + onError = { errorMsg -> + error = errorMsg + refreshing = false + } + ) + } + ) + + LaunchedEffect(Unit) { + isLoading = true + refreshAllData(context, + onSuccess = { conversations -> + allConversations = conversations + isLoading = false + }, + onError = { errorMsg -> + error = errorMsg + isLoading = false + } + ) + } + + Column( + modifier = Modifier + .fillMaxSize() + .background(AppColors.background) + ) { + Box( + modifier = Modifier + .fillMaxSize() + .pullRefresh(state) + ) { + if (allConversations.isEmpty() && !isLoading) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + val isNetworkAvailable = NetworkUtils.isNetworkAvailable(context) + + if (isNetworkAvailable) { + Spacer(modifier = Modifier.height(39.dp)) + Image( + painter = painterResource( + id = if(AppState.darkMode) R.mipmap.qs_py_qs_as_img + else R.mipmap.invalid_name_2), + contentDescription = "null data", + modifier = Modifier + .size(181.dp) + ) + Spacer(modifier = Modifier.height(24.dp)) + Text( + text = stringResource(R.string.friend_chat_empty_title), + color = AppColors.text, + fontSize = 16.sp, + fontWeight = FontWeight.W600 + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = stringResource(R.string.friend_chat_empty_subtitle), + color = AppColors.secondaryText, + fontSize = 14.sp + ) + } else { + Spacer(modifier = Modifier.height(39.dp)) + Image( + painter = painterResource(id = R.mipmap.invalid_name_10), + contentDescription = "network error", + modifier = Modifier + .size(181.dp) + ) + Spacer(modifier = Modifier.height(24.dp)) + Text( + text = stringResource(R.string.friend_chat_no_network_title), + color = AppColors.text, + fontSize = 16.sp, + fontWeight = FontWeight.W600 + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = stringResource(R.string.friend_chat_no_network_subtitle), + color = AppColors.secondaryText, + fontSize = 14.sp + ) + Spacer(modifier = Modifier.height(16.dp)) + ReloadButton( + onClick = { + isLoading = true + refreshAllData(context, + onSuccess = { conversations -> + allConversations = conversations + isLoading = false + }, + onError = { errorMsg -> + error = errorMsg + isLoading = false + } + ) + } + ) + } + } + } else { + LazyColumn( + modifier = Modifier.fillMaxSize() + ) { + itemsIndexed( + items = allConversations, + key = { _, item -> item.id } + ) { index, item -> + when (item.type) { + "agent" -> { + item.agentConversation?.let { agent -> + AgentChatItem( + conversation = agent, + onUserAvatarClick = { conv -> + AgentChatListViewModel.goToUserDetail(conv, navController) + }, + onChatClick = { conv -> + if (NetworkUtils.isNetworkAvailable(context)) { + AgentChatListViewModel.createSingleChat(conv.trtcUserId) + AgentChatListViewModel.goToChatAi(conv.trtcUserId, navController) + } else { + Toast.makeText(context, "网络连接异常,请检查网络设置", Toast.LENGTH_SHORT).show() + } + } + ) + } + } + "group" -> { + item.groupConversation?.let { group -> + GroupChatItem( + conversation = group, + onGroupAvatarClick = { conv -> + GroupChatListViewModel.goToGroupDetail(conv, navController) + }, + onChatClick = { conv -> + if (NetworkUtils.isNetworkAvailable(context)) { + GroupChatListViewModel.goToChat(conv, navController) + } else { + Toast.makeText(context, "网络连接异常,请检查网络设置", Toast.LENGTH_SHORT).show() + } + } + ) + } + } + "friend" -> { + item.friendConversation?.let { friend -> + FriendChatItem( + conversation = friend, + onUserAvatarClick = { conv -> + FriendChatListViewModel.goToUserDetail(conv, navController) + }, + onChatClick = { conv -> + if (NetworkUtils.isNetworkAvailable(context)) { + FriendChatListViewModel.goToChat(conv, navController) + } else { + Toast.makeText(context, "网络连接异常,请检查网络设置", Toast.LENGTH_SHORT).show() + } + } + ) + } + } + } + + // 分隔线 +// if (index < allConversations.size - 1) { +// HorizontalDivider( +// modifier = Modifier.padding(horizontal = 24.dp), +// color = AppColors.divider +// ) +// } + } + + if (isLoading && allConversations.isNotEmpty()) { + item { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator( + modifier = Modifier.size(24.dp), + color = AppColors.main + ) + } + } + } + } + } + + PullRefreshIndicator( + refreshing = refreshing, + state = state, + modifier = Modifier.align(Alignment.TopCenter) + ) + } + + error?.let { errorMsg -> + Box( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + contentAlignment = Alignment.Center + ) { + Text( + text = errorMsg, + color = AppColors.error, + fontSize = 14.sp + ) + } + } + } +} + +fun refreshAllData( + context: android.content.Context, + onSuccess: (List) -> Unit, + onError: (String) -> Unit +) { + try { + // 同时刷新所有类型的数据 + AgentChatListViewModel.refreshPager(context = context) + GroupChatListViewModel.refreshPager(context = context) + FriendChatListViewModel.refreshPager(context = context) + + val combinedList = mutableListOf() + + AgentChatListViewModel.agentChatList.forEach { agent -> + combinedList.add(CombinedConversation(type = "agent", agentConversation = agent)) + } + + GroupChatListViewModel.groupChatList.forEach { group -> + combinedList.add(CombinedConversation(type = "group", groupConversation = group)) + } + + FriendChatListViewModel.friendChatList.forEach { friend -> + val isDuplicate = combinedList.any {//判断重复 + it.type == "agent" && it.agentConversation?.trtcUserId == friend.trtcUserId + } + + if (!isDuplicate) { + combinedList.add(CombinedConversation(type = "friend", friendConversation = friend)) + } + } + + // 按最后消息时间排序 + val sortedList = combinedList.sortedByDescending { + it.lastMessageTime + } + + onSuccess(sortedList) + } catch (e: Exception) { + onError("刷新数据失败: ${e.message}") + } +} diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/profile/composable/SelfProfileAction.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/profile/composable/SelfProfileAction.kt index 6383bc3..05c9454 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/profile/composable/SelfProfileAction.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/profile/composable/SelfProfileAction.kt @@ -48,7 +48,7 @@ fun SelfProfileAction( horizontalArrangement = Arrangement.Center, modifier = Modifier .weight(1f) - .clip(RoundedCornerShape(18.dp)) + .clip(RoundedCornerShape(10.dp)) .background(AppColors.nonActive) .padding(horizontal = 16.dp, vertical = 12.dp) .noRippleClickable { @@ -60,7 +60,27 @@ fun SelfProfileAction( Text( text = stringResource(R.string.edit_profile), fontSize = 14.sp, - fontWeight = FontWeight.W600, + fontWeight = FontWeight.W900, + color = AppColors.text, + ) + } + + // 预留按钮位置 + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = Modifier + .weight(1f) + .clip(RoundedCornerShape(10.dp)) + .padding(horizontal = 16.dp, vertical = 12.dp) + .noRippleClickable { + + } + ) { + Text( + text = "", + fontSize = 14.sp, + fontWeight = FontWeight.W900, color = AppColors.text, ) } @@ -71,7 +91,7 @@ fun SelfProfileAction( horizontalArrangement = Arrangement.Center, modifier = Modifier .weight(1f) - .clip(RoundedCornerShape(18.dp)) + .clip(RoundedCornerShape(10.dp)) .background(AppColors.nonActive) .padding(horizontal = 16.dp, vertical = 12.dp) .noRippleClickable { @@ -83,7 +103,7 @@ fun SelfProfileAction( Text( text = stringResource(R.string.share), fontSize = 14.sp, - fontWeight = FontWeight.W600, + fontWeight = FontWeight.W900, color = AppColors.text, ) } diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index bf64abb..de4ba90 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -146,6 +146,7 @@ AI グループ 友達 + すべて AIエージェントチャット AIエージェントチャットがありません AIエージェントと対話してみましょう diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 7f4a9e3..738a091 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -48,7 +48,7 @@ "为了提供更好的服务,请您在注册前仔细阅读并同意《用户协议》。 " 还没有发布任何动态 发布一个动态吧 - 编辑个人资料 + 编辑资料 分享 登出 修改密码 @@ -148,6 +148,7 @@ 智能体 群聊 朋友 + 全部 咦,什么都没有... 智能体聊天 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3d5b761..dced41b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -145,6 +145,7 @@ Ai Group Friends + All Agent Chat No Agent Chat Start chatting with agents