Merge upstream/main and resolve conflicts

This commit is contained in:
2025-08-22 10:41:30 +08:00
31 changed files with 1056 additions and 257 deletions

2
.idea/kotlinc.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="KotlinJpsPluginSettings"> <component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.0" /> <option name="version" value="1.9.10" />
</component> </component>
</project> </project>

View File

@@ -41,7 +41,9 @@ data class AgentMomentRequestBody(
data class SingleChatRequestBody( data class SingleChatRequestBody(
@SerializedName("agentOpenId") @SerializedName("agentOpenId")
val generateText: String, val generateText: String? = null,
@SerializedName("agentTrtcId")
val agentTrtcId: String? = null,
) )
data class GroupChatRequestBody( data class GroupChatRequestBody(
@@ -561,6 +563,11 @@ interface RaveNowAPI {
@Query("isRecommended") isRecommended: Int = 1, @Query("isRecommended") isRecommended: Int = 1,
): Response<ListContainer<Room>> ): Response<ListContainer<Room>>
@GET("outside/rooms/detail")
suspend fun getRoomDetail(@Query("trtcId") trtcId: String,
): Response<DataContainer<Room>>
} }

View File

@@ -0,0 +1,15 @@
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,
)

View File

@@ -35,6 +35,7 @@ import com.aiosman.ravenow.ui.agent.AddAgentScreen
import com.aiosman.ravenow.ui.group.CreateGroupChatScreen import com.aiosman.ravenow.ui.group.CreateGroupChatScreen
import com.aiosman.ravenow.ui.chat.ChatAiScreen import com.aiosman.ravenow.ui.chat.ChatAiScreen
import com.aiosman.ravenow.ui.chat.ChatScreen 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.chat.GroupChatScreen
import com.aiosman.ravenow.ui.comment.CommentsScreen import com.aiosman.ravenow.ui.comment.CommentsScreen
import com.aiosman.ravenow.ui.comment.notice.CommentNoticeScreen import com.aiosman.ravenow.ui.comment.notice.CommentNoticeScreen
@@ -96,6 +97,7 @@ sealed class NavigationRoute(
data object Chat : NavigationRoute("Chat/{id}") data object Chat : NavigationRoute("Chat/{id}")
data object ChatAi : NavigationRoute("ChatAi/{id}") data object ChatAi : NavigationRoute("ChatAi/{id}")
data object ChatGroup : NavigationRoute("ChatGroup/{id}/{name}/{avatar}") data object ChatGroup : NavigationRoute("ChatGroup/{id}/{name}/{avatar}")
data object GroupChatInfo : NavigationRoute("GroupChatInfo/{groupId}")
data object CommentNoticeScreen : NavigationRoute("CommentNoticeScreen") data object CommentNoticeScreen : NavigationRoute("CommentNoticeScreen")
data object ImageCrop : NavigationRoute("ImageCrop") data object ImageCrop : NavigationRoute("ImageCrop")
data object AccountSetting : NavigationRoute("AccountSetting") data object AccountSetting : NavigationRoute("AccountSetting")
@@ -404,6 +406,19 @@ fun NavigationController(
} }
} }
composable(
route = NavigationRoute.GroupChatInfo.route,
arguments = listOf(navArgument("groupId") { type = NavType.StringType })
) {
val encodedId = it.arguments?.getString("groupId")
val decodedId = encodedId?.let { java.net.URLDecoder.decode(it, "UTF-8") }
CompositionLocalProvider(
LocalAnimatedContentScope provides this,
) {
GroupChatInfoScreen(decodedId?:"")
}
}
composable(route = NavigationRoute.CommentNoticeScreen.route) { composable(route = NavigationRoute.CommentNoticeScreen.route) {
CompositionLocalProvider( CompositionLocalProvider(
@@ -514,6 +529,15 @@ fun NavHostController.navigateToGroupChat(id: String,name:String,avatar:String)
) )
} }
fun NavHostController.navigateToGroupInfo(id: String) {
val encodedId = java.net.URLEncoder.encode(id, "UTF-8")
navigate(
route = NavigationRoute.GroupChatInfo.route
.replace("{groupId}", encodedId)
)
}
fun NavHostController.goTo( fun NavHostController.goTo(

View File

@@ -8,6 +8,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.Crossfade import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
@@ -49,6 +50,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.focus.onFocusChanged
@@ -175,7 +177,7 @@ fun ChatScreen(userId: String) {
Image( Image(
painter = painterResource(R.drawable.rider_pro_back_icon), painter = painterResource(R.drawable.rider_pro_back_icon),
modifier = Modifier modifier = Modifier
.size(28.dp) .size(24.dp)
.noRippleClickable { .noRippleClickable {
navController.navigateUp() navController.navigateUp()
}, },
@@ -187,8 +189,8 @@ fun ChatScreen(userId: String) {
CustomAsyncImage( CustomAsyncImage(
imageUrl = viewModel.userProfile?.avatar ?: "", imageUrl = viewModel.userProfile?.avatar ?: "",
modifier = Modifier modifier = Modifier
.size(40.dp) .size(32.dp)
.clip(RoundedCornerShape(40.dp)), .clip(RoundedCornerShape(32.dp)),
contentDescription = "avatar" contentDescription = "avatar"
) )
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
@@ -221,7 +223,7 @@ fun ChatScreen(userId: String) {
}, },
menuItems = listOf( menuItems = listOf(
MenuItem( MenuItem(
title = if (viewModel.notificationStrategy == "mute") "Unmute" else "Mute", title = if (viewModel.notificationStrategy == "mute") "" else "",
icon = if (viewModel.notificationStrategy == "mute") R.drawable.rider_pro_notice_mute else R.drawable.rider_pro_notice_active, icon = if (viewModel.notificationStrategy == "mute") R.drawable.rider_pro_notice_mute else R.drawable.rider_pro_notice_active,
) { ) {
@@ -251,8 +253,7 @@ fun ChatScreen(userId: String) {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(1.dp) .height(1.dp)
.background(
AppColors.decentBackground)
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
ChatInput( ChatInput(
@@ -270,13 +271,12 @@ fun ChatScreen(userId: String) {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(AppColors.decentBackground) .background(AppColors.background)
.padding(paddingValues) .padding(paddingValues)
) { ) {
LazyColumn( LazyColumn(
state = listState, state = listState,
modifier = Modifier modifier = Modifier.fillMaxSize(),
.fillMaxSize(),
reverseLayout = true, reverseLayout = true,
verticalArrangement = Arrangement.Top verticalArrangement = Arrangement.Top
) { ) {
@@ -340,6 +340,7 @@ fun ChatScreen(userId: String) {
@Composable @Composable
fun ChatSelfItem(item: ChatItem) { fun ChatSelfItem(item: ChatItem) {
val context = LocalContext.current val context = LocalContext.current
Column( Column(
modifier = Modifier modifier = Modifier
@@ -353,19 +354,28 @@ fun ChatSelfItem(item: ChatItem) {
Column( Column(
horizontalAlignment = androidx.compose.ui.Alignment.End, horizontalAlignment = androidx.compose.ui.Alignment.End,
) { ) {
/* Text(
text = item.nickname,
style = TextStyle(
color = Color.Gray,
fontSize = 12.sp,
),
modifier = Modifier.padding(bottom = 2.dp)
)
*/
Box( Box(
modifier = Modifier modifier = Modifier
.widthIn( .widthIn(
min = 20.dp, min = 20.dp,
max = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 250.dp else 150.dp) max = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 250.dp else 150.dp)
) )
.clip(RoundedCornerShape(8.dp)) .clip(RoundedCornerShape(20.dp))
.background(Color(0xFF000000)) .background(Color(0xFF6246FF))
.padding( .padding(
vertical = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 8.dp else 0.dp), 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) 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) { when (item.messageType) {
V2TIMMessage.V2TIM_ELEM_TYPE_TEXT -> { V2TIMMessage.V2TIM_ELEM_TYPE_TEXT -> {
@@ -373,7 +383,7 @@ fun ChatSelfItem(item: ChatItem) {
text = item.message, text = item.message,
style = TextStyle( style = TextStyle(
color = Color.White, color = Color.White,
fontSize = 16.sp, fontSize = 14.sp,
), ),
textAlign = TextAlign.Start textAlign = TextAlign.Start
) )
@@ -389,28 +399,28 @@ fun ChatSelfItem(item: ChatItem) {
else -> { else -> {
Text( Text(
text = "Unsupported message type", text = "不支持的消息类型",
style = TextStyle( style = TextStyle(
color = Color.White, color = Color.White,
fontSize = 16.sp, fontSize = 14.sp,
) )
) )
} }
} }
} }
} }
Spacer(modifier = Modifier.width(12.dp)) /*Spacer(modifier = Modifier.width(12.dp))
Box( Box(
modifier = Modifier modifier = Modifier
.size(40.dp) .size(24.dp)
.clip(RoundedCornerShape(40.dp)) .clip(RoundedCornerShape(24.dp))
) { ) {
CustomAsyncImage( CustomAsyncImage(
imageUrl = item.avatar, imageUrl = item.avatar,
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentDescription = "avatar" contentDescription = "avatar"
) )
} }*/
} }
} }
} }
@@ -430,11 +440,11 @@ fun ChatOtherItem(item: ChatItem) {
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
.size(40.dp) .size(24.dp)
.clip(RoundedCornerShape(40.dp)) .clip(RoundedCornerShape(24.dp))
) { ) {
CustomAsyncImage( CustomAsyncImage(
imageUrl = item.avatar, imageUrl = item.avatar.replace("storage/avatars/", "/avatar/"),
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentDescription = "avatar" contentDescription = "avatar"
) )
@@ -447,13 +457,12 @@ fun ChatOtherItem(item: ChatItem) {
min = 20.dp, min = 20.dp,
max = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 250.dp else 150.dp) max = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 250.dp else 150.dp)
) )
.clip(RoundedCornerShape(8.dp)) .clip(RoundedCornerShape(20.dp))
.background(AppColors.background) .background(AppColors.bubbleBackground)
.padding( .padding(
vertical = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 8.dp else 0.dp), 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) 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) { when (item.messageType) {
V2TIMMessage.V2TIM_ELEM_TYPE_TEXT -> { V2TIMMessage.V2TIM_ELEM_TYPE_TEXT -> {
@@ -461,7 +470,7 @@ fun ChatOtherItem(item: ChatItem) {
text = item.message, text = item.message,
style = TextStyle( style = TextStyle(
color = AppColors.text, color = AppColors.text,
fontSize = 16.sp, fontSize = 14.sp,
), ),
textAlign = TextAlign.Start textAlign = TextAlign.Start
) )
@@ -477,7 +486,7 @@ fun ChatOtherItem(item: ChatItem) {
else -> { else -> {
Text( Text(
text = "Unsupported message type", text = "不支持的消息类型",
style = TextStyle( style = TextStyle(
color = AppColors.text, color = AppColors.text,
fontSize = 16.sp, fontSize = 16.sp,
@@ -486,7 +495,6 @@ fun ChatOtherItem(item: ChatItem) {
} }
} }
} }
} }
} }
@@ -547,20 +555,21 @@ fun ChatInput(
onSendImage(uri) onSendImage(uri)
} }
} }
Box( modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, bottom = 12.dp),){
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp) .clip(RoundedCornerShape(20.dp))
.padding(bottom = inputBarHeight) .background(appColors.decentBackground)
.padding(start = 16.dp, end = 8.dp, top = 2.dp, bottom = 2.dp),
verticalAlignment = Alignment.CenterVertically,
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.clip(RoundedCornerShape(16.dp))
.background(appColors.background)
.padding(horizontal = 16.dp),
contentAlignment = Alignment.CenterStart,
) { ) {
BasicTextField( BasicTextField(
value = text, value = text,
@@ -572,6 +581,7 @@ fun ChatInput(
fontSize = 16.sp fontSize = 16.sp
), ),
cursorBrush = SolidColor(appColors.text), cursorBrush = SolidColor(appColors.text),
singleLine = true,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = 8.dp) .padding(vertical = 8.dp)
@@ -594,10 +604,10 @@ fun ChatInput(
) )
) )
} }
Spacer(modifier = Modifier.width(16.dp)) Spacer(modifier = Modifier.width(8.dp))
Icon( Image(
painter = painterResource(id = R.drawable.rider_pro_camera), painter = painterResource(R.mipmap.rider_pro_im_image),
contentDescription = "Emoji", contentDescription = "Image",
modifier = Modifier modifier = Modifier
.size(30.dp) .size(30.dp)
.noRippleClickable { .noRippleClickable {
@@ -610,28 +620,33 @@ fun ChatInput(
) )
) )
}, },
tint = appColors.chatActionColor
) )
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
Crossfade( Crossfade(
targetState = text.isNotEmpty(), animationSpec = tween(500), targetState = text.isNotEmpty(), animationSpec = tween(500),
label = "" label = ""
) { isNotEmpty -> ) { isNotEmpty ->
Icon( val alpha by animateFloatAsState(
painter = painterResource(id = R.drawable.rider_pro_video_share), targetValue = if (isNotEmpty) 1f else 0.5f,
contentDescription = "Emoji", animationSpec = tween(300)
)
Image(
painter = painterResource(R.mipmap.rider_pro_im_send),
modifier = Modifier modifier = Modifier
.size(32.dp) .size(24.dp)
.alpha(alpha)
.noRippleClickable { .noRippleClickable {
if (text.isNotEmpty()) { if (text.isNotEmpty()) {
onSend(text) onSend(text)
text = "" text = ""
} }
}, },
tint = if (isNotEmpty) appColors.main else appColors.chatActionColor contentDescription = null,
) )
} }
} }
}
} }
fun groupMessagesByTime(chatList: List<ChatItem>, viewModel: ChatViewModel): List<ChatItem> { fun groupMessagesByTime(chatList: List<ChatItem>, viewModel: ChatViewModel): List<ChatItem> {

View File

@@ -8,6 +8,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.Crossfade import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
@@ -49,6 +50,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.focus.onFocusChanged
@@ -189,7 +191,7 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) {
} else { } else {
Box( Box(
modifier = Modifier modifier = Modifier
.size(40.dp) .size(24.dp)
.clip(RoundedCornerShape(8.dp)) .clip(RoundedCornerShape(8.dp))
.background(AppColors.decentBackground), .background(AppColors.decentBackground),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
@@ -234,34 +236,6 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) {
contentDescription = null, contentDescription = null,
colorFilter = ColorFilter.tint(AppColors.text) colorFilter = ColorFilter.tint(AppColors.text)
) )
DropdownMenu(
expanded = isMenuExpanded,
onDismissRequest = {
isMenuExpanded = false
},
menuItems = listOf(
MenuItem(
title = if (viewModel.notificationStrategy == "mute") "取消静音" else "静音",
icon = if (viewModel.notificationStrategy == "mute") R.drawable.rider_pro_notice_mute else R.drawable.rider_pro_notice_active,
) {
isMenuExpanded = false
viewModel.viewModelScope.launch {
if (viewModel.notificationStrategy == "mute") {
viewModel.updateNotificationStrategy("active")
} else {
viewModel.updateNotificationStrategy("mute")
}
}
},
MenuItem(
title = "群成员",
icon = R.drawable.rider_pro_more_horizon,
) {
isMenuExpanded = false
viewModel.getGroupMembers()
}
),
)
} }
} }
} }
@@ -276,9 +250,8 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(1.dp) .height(1.dp)
.background(AppColors.decentBackground)
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(12.dp))
GroupChatInput( GroupChatInput(
onSendImage = { uri -> onSendImage = { uri ->
uri?.let { uri?.let {
@@ -294,7 +267,7 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(AppColors.decentBackground) .background(AppColors.background)
.padding(paddingValues) .padding(paddingValues)
) { ) {
LazyColumn( LazyColumn(
@@ -306,6 +279,7 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) {
val chatList = groupMessagesByTime(viewModel.getDisplayChatList(), viewModel) val chatList = groupMessagesByTime(viewModel.getDisplayChatList(), viewModel)
items(chatList.size, key = { index -> chatList[index].msgId + UUID.randomUUID().toString()}) { index -> items(chatList.size, key = { index -> chatList[index].msgId + UUID.randomUUID().toString()}) { index ->
val item = chatList[index] val item = chatList[index]
Column {
if (item.showTimeDivider) { if (item.showTimeDivider) {
val calendar = java.util.Calendar.getInstance() val calendar = java.util.Calendar.getInstance()
calendar.timeInMillis = item.timestamp calendar.timeInMillis = item.timestamp
@@ -313,7 +287,7 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) {
text = calendar.time.formatChatTime(context), text = calendar.time.formatChatTime(context),
style = TextStyle( style = TextStyle(
color = AppColors.secondaryText, color = AppColors.secondaryText,
fontSize = 14.sp, fontSize = 11.sp,
textAlign = TextAlign.Center textAlign = TextAlign.Center
), ),
modifier = Modifier modifier = Modifier
@@ -321,8 +295,15 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) {
.padding(vertical = 8.dp) .padding(vertical = 8.dp)
) )
} }
// 获取上一个item的userId用于判断是否显示头像和昵称
GroupChatItem(item = item, viewModel.myProfile?.trtcUserId!!) 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
)
}
} }
} }
@@ -345,7 +326,7 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) {
text = "${goToNewCount} 条新消息", text = "${goToNewCount} 条新消息",
style = TextStyle( style = TextStyle(
color = AppColors.text, color = AppColors.text,
fontSize = 16.sp, fontSize = 12.sp,
), ),
) )
} }
@@ -390,7 +371,7 @@ fun GroupChatSelfItem(item: ChatItem) {
vertical = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 8.dp else 0.dp), 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) 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) { when (item.messageType) {
V2TIMMessage.V2TIM_ELEM_TYPE_TEXT -> { V2TIMMessage.V2TIM_ELEM_TYPE_TEXT -> {
@@ -441,7 +422,7 @@ fun GroupChatSelfItem(item: ChatItem) {
} }
@Composable @Composable
fun GroupChatOtherItem(item: ChatItem) { fun GroupChatOtherItem(item: ChatItem, showAvatarAndNickname: Boolean = true) {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
Column( Column(
@@ -453,6 +434,7 @@ fun GroupChatOtherItem(item: ChatItem) {
horizontalArrangement = Arrangement.Start, horizontalArrangement = Arrangement.Start,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
if (showAvatarAndNickname) {
Box( Box(
modifier = Modifier modifier = Modifier
.size(24.dp) .size(24.dp)
@@ -465,16 +447,11 @@ fun GroupChatOtherItem(item: ChatItem) {
) )
} }
Spacer(modifier = Modifier.width(12.dp)) Spacer(modifier = Modifier.width(12.dp))
} else {
// 当不显示头像时,添加左边距以保持消息对齐
Spacer(modifier = Modifier.width(36.dp))
}
Column { Column {
Text(
text = item.nickname,
style = TextStyle(
color = AppColors.secondaryText,
fontSize = 12.sp,
),
modifier = Modifier.padding(bottom = 2.dp)
)
Box( Box(
modifier = Modifier modifier = Modifier
.widthIn( .widthIn(
@@ -487,7 +464,6 @@ fun GroupChatOtherItem(item: ChatItem) {
vertical = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 8.dp else 0.dp), 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) 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) { when (item.messageType) {
V2TIMMessage.V2TIM_ELEM_TYPE_TEXT -> { V2TIMMessage.V2TIM_ELEM_TYPE_TEXT -> {
@@ -520,13 +496,24 @@ fun GroupChatOtherItem(item: ChatItem) {
} }
} }
} }
if (showAvatarAndNickname) {
Text(
text = item.nickname,
style = TextStyle(
color = AppColors.secondaryText,
fontSize = 12.sp,
),
modifier = Modifier.padding(bottom = 2.dp)
)
}
} }
} }
} }
} }
@Composable @Composable
fun GroupChatItem(item: ChatItem, currentUserId: String) { fun GroupChatItem(item: ChatItem, currentUserId: String, showAvatarAndNickname: Boolean = true) {
val isCurrentUser = item.userId == currentUserId val isCurrentUser = item.userId == currentUserId
// 管理员消息显示特殊布局 // 管理员消息显示特殊布局
@@ -538,7 +525,7 @@ fun GroupChatItem(item: ChatItem, currentUserId: String) {
// 根据是否是当前用户显示不同样式 // 根据是否是当前用户显示不同样式
when (item.userId) { when (item.userId) {
currentUserId -> GroupChatSelfItem(item) currentUserId -> GroupChatSelfItem(item)
else -> GroupChatOtherItem(item) else -> GroupChatOtherItem(item, showAvatarAndNickname)
} }
} }
@@ -615,20 +602,21 @@ fun GroupChatInput(
onSendImage(uri) onSendImage(uri)
} }
} }
Box( modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, bottom = 12.dp),){
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp) .clip(RoundedCornerShape(20.dp))
.padding(bottom = inputBarHeight) .background(appColors.decentBackground)
.padding(start = 16.dp, end = 8.dp, top = 2.dp, bottom = 2.dp),
verticalAlignment = Alignment.CenterVertically,
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.clip(RoundedCornerShape(16.dp))
.background(appColors.background)
.padding(horizontal = 16.dp),
contentAlignment = Alignment.CenterStart,
) { ) {
BasicTextField( BasicTextField(
value = text, value = text,
@@ -640,6 +628,7 @@ fun GroupChatInput(
fontSize = 16.sp fontSize = 16.sp
), ),
cursorBrush = SolidColor(appColors.text), cursorBrush = SolidColor(appColors.text),
singleLine = true,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = 8.dp) .padding(vertical = 8.dp)
@@ -662,44 +651,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)) Spacer(modifier = Modifier.width(8.dp))
Crossfade( Crossfade(
targetState = text.isNotEmpty(), animationSpec = tween(500), targetState = text.isNotEmpty(), animationSpec = tween(500),
label = "" label = ""
) { isNotEmpty -> ) { isNotEmpty ->
Icon( val alpha by animateFloatAsState(
painter = painterResource(id = R.drawable.rider_pro_video_share), targetValue = if (isNotEmpty) 1f else 0.5f,
contentDescription = "发送消息", animationSpec = tween(300)
)
Image(
painter = painterResource(R.mipmap.rider_pro_im_send),
modifier = Modifier modifier = Modifier
.size(32.dp) .size(24.dp)
.alpha(alpha)
.noRippleClickable { .noRippleClickable {
if (text.isNotEmpty()) { if (text.isNotEmpty()) {
onSend(text) onSend(text)
text = "" text = ""
} }
}, },
tint = if (isNotEmpty) appColors.main else appColors.chatActionColor contentDescription = null,
) )
} }
} }
}
} }
fun groupMessagesByTime(chatList: List<ChatItem>, viewModel: GroupChatViewModel): List<ChatItem> { fun groupMessagesByTime(chatList: List<ChatItem>, viewModel: GroupChatViewModel): List<ChatItem> {
@@ -711,7 +688,7 @@ fun groupMessagesByTime(chatList: List<ChatItem>, viewModel: GroupChatViewModel)
} }
val currentMessage = chatList[i] val currentMessage = chatList[i]
val timeDiff = currentMessage.timestamp - chatList[i - 1].timestamp val timeDiff = currentMessage.timestamp - chatList[i - 1].timestamp
if (-timeDiff > 30 * 60 * 1000) { if (-timeDiff > 10 * 60 * 1000) {
viewModel.showTimestampMap[currentMessage.msgId] = true viewModel.showTimestampMap[currentMessage.msgId] = true
currentMessage.showTimeDivider = true currentMessage.showTimeDivider = true
} }

View File

@@ -48,7 +48,6 @@ class GroupChatViewModel(
var isLoading by mutableStateOf(false) var isLoading by mutableStateOf(false)
var lastMessage: V2TIMMessage? = null var lastMessage: V2TIMMessage? = null
val showTimestampMap = mutableMapOf<String, Boolean>() val showTimestampMap = mutableMapOf<String, Boolean>()
var chatNotification by mutableStateOf<ChatNotification?>(null)
var goToNew by mutableStateOf(false) var goToNew by mutableStateOf(false)
// 群聊特有属性 // 群聊特有属性
@@ -71,8 +70,6 @@ class GroupChatViewModel(
myProfile = accountService.getMyAccountProfile() myProfile = accountService.getMyAccountProfile()
RegistListener(context) RegistListener(context)
fetchHistoryMessage(context) fetchHistoryMessage(context)
val notiStrategy = ChatState.getStrategyByTargetTrtcId(groupId)
chatNotification = notiStrategy
} catch (e: Exception) { } catch (e: Exception) {
Log.e("GroupChatViewModel", "初始化失败: ${e.message}") Log.e("GroupChatViewModel", "初始化失败: ${e.message}")
} }
@@ -287,16 +284,4 @@ class GroupChatViewModel(
return list return list
} }
suspend fun updateNotificationStrategy(strategy: String) {
val result = ChatState.updateChatNotification(groupId.hashCode(), strategy)
chatNotification = result
}
val notificationStrategy get() = chatNotification?.strategy ?: "default"
// 群聊特有功能
fun getGroupMembers() {
// 简化群成员获取,暂时只记录日志
Log.d("GroupChatViewModel", "获取群成员功能待实现")
}
} }

View File

@@ -0,0 +1,328 @@
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.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
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<GroupChatInfoViewModel>(
key = "GroupChatInfoViewModel_$groupId",
factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): 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(),
textAlign = TextAlign.Start,
text = "群聊信息",
style = androidx.compose.ui.text.TextStyle(
color = AppColors.text,
fontSize = 17.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(RoundedCornerShape(12.dp)),
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
)
)
}
}
// 操作按钮
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),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(R.drawable.rider_pro_add_other),
modifier = Modifier.size(24.dp),
contentDescription = null,
colorFilter = ColorFilter.tint(
AppColors.text)
)
}
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(R.string.group_info_add_other),
style = androidx.compose.ui.text.TextStyle(
color = AppColors.text,
fontSize = 12.sp
)
)
}
// 通知设置
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.noRippleClickable {
/* if (viewModel.notificationStrategy == "mute") {
viewModel.updateNotificationStrategy("active")
} else {
viewModel.updateNotificationStrategy("mute")
}*/
}
) {
Box(
modifier = Modifier
.size(48.dp)
.clip(CircleShape),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(if (viewModel.notificationStrategy == "mute") R.drawable.rider_pro_notice_mute else R.drawable.rider_pro_notice_active,),
modifier = Modifier.size(24.dp),
contentDescription = null,
colorFilter = ColorFilter.tint(
AppColors.text)
)
}
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(R.string.group_info_notice_setting),
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),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(R.drawable.group_info_exit
),
modifier = Modifier.size(24.dp),
contentDescription = null,
colorFilter = ColorFilter.tint(
AppColors.text)
)
}
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(R.string.group_info_exit),
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))
.padding(16.dp)
.noRippleClickable {
// TODO: 实现设置聊天主题功能
},
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(R.drawable.group_info_edit),
modifier = Modifier.size(24.dp),
contentDescription = null,
colorFilter = ColorFilter.tint(
AppColors.text)
)
Spacer(modifier = Modifier.width(12.dp))
Text(
text = stringResource(R.string.group_info_edit),
style = androidx.compose.ui.text.TextStyle(
color = AppColors.text,
fontSize = 16.sp
),
modifier = Modifier.weight(1f)
)
Image(
painter = painterResource(R.drawable.rave_now_nav_right),
modifier = Modifier.size(18.dp),
contentDescription = null,
)
}
Spacer(modifier = Modifier.height(1.dp))
// 群聊成员
Row(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(8.dp))
.padding(16.dp)
.noRippleClickable {
// TODO: 实现查看群聊成员功能
},
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(R.drawable.group_info_users),
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.rave_now_nav_right),
modifier = Modifier.size(18.dp),
contentDescription = null,
)
}
}
}
}
}

View File

@@ -0,0 +1,72 @@
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.AppStore
import com.aiosman.ravenow.ChatState
import com.aiosman.ravenow.data.api.ApiClient
import com.aiosman.ravenow.entity.ChatNotification
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<GroupInfo?>(null)
var isLoading by mutableStateOf(false)
var error by mutableStateOf<String?>(null)
var chatNotification by mutableStateOf<ChatNotification?>(null)
val notificationStrategy get() = chatNotification?.strategy ?: "default"
init {
loadGroupInfo()
}
suspend fun updateNotificationStrategy(strategy: String) {
val result = ChatState.updateChatNotification(groupId.hashCode(), strategy)
chatNotification = result
}
private fun loadGroupInfo() {
viewModelScope.launch {
val notiStrategy = ChatState.getStrategyByTargetTrtcId(groupId)
chatNotification = notiStrategy
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 = if (it.avatar.isNullOrEmpty()) {
// 将 groupId 转换为 Base64
val groupIdBase64 = android.util.Base64.encodeToString(
groupId.toByteArray(),
android.util.Base64.NO_WRAP
)
"${ApiClient.RETROFIT_URL+"group/avatar?groupIdBase64="}${groupIdBase64}"+"&token="+"${AppStore.token}"
} else {
"${ApiClient.BASE_API_URL+"/outside/rooms/avatar/"}${it.avatar}"+"?token="+"${AppStore.token}"
},
memberCount = room.userCount,
)
}
} catch (e: Exception) {
error = e.message ?: "加载失败"
} finally {
isLoading = false
}
}
}
}

View File

@@ -21,8 +21,10 @@ import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.DrawerValue import androidx.compose.material3.DrawerValue
import androidx.compose.material3.Icon 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.text.font.FontWeight
import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.offset
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.ravenow.AppState import com.aiosman.ravenow.AppState
import com.aiosman.ravenow.AppStore 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.add.AddPage
import com.aiosman.ravenow.ui.index.tabs.ai.Agent 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.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.moment.MomentsList
import com.aiosman.ravenow.ui.index.tabs.profile.ProfileWrap import com.aiosman.ravenow.ui.index.tabs.profile.ProfileWrap
import com.aiosman.ravenow.ui.index.tabs.search.DiscoverScreen import com.aiosman.ravenow.ui.index.tabs.search.DiscoverScreen
@@ -99,6 +105,10 @@ fun IndexScreen() {
val context = LocalContext.current val context = LocalContext.current
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
systemUiController.setNavigationBarColor(Color.Transparent) systemUiController.setNavigationBarColor(Color.Transparent)
// 初始化聊天列表以获取未读消息数
AgentChatListViewModel.refreshPager(context = context)
GroupChatListViewModel.refreshPager(context = context)
FriendChatListViewModel.refreshPager(context = context)
} }
LaunchedEffect(model.openDrawer) { LaunchedEffect(model.openDrawer) {
if (model.openDrawer) { if (model.openDrawer) {
@@ -310,6 +320,13 @@ fun IndexScreen() {
shape = RoundedCornerShape(12.dp) shape = RoundedCornerShape(12.dp)
), ),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.width(24.dp)
.height(24.dp)
,
contentAlignment = Alignment.Center
) { ) {
Image( Image(
painter = painterResource(if (isSelected) it.selectedIcon() else it.icon()), painter = painterResource(if (isSelected) it.selectedIcon() else it.icon()),
@@ -317,6 +334,27 @@ fun IndexScreen() {
modifier = Modifier.size(24.dp), modifier = Modifier.size(24.dp),
colorFilter = if (!isSelected) ColorFilter.tint(AppColors.text) else null 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)
)
}
}
}
} }
// 文字标签,可控制间距 // 文字标签,可控制间距

View File

@@ -16,6 +16,8 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBars 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.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.offset
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.aiosman.ravenow.AppState import com.aiosman.ravenow.AppState
import com.aiosman.ravenow.AppStore 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.composables.TabSpacer
import com.aiosman.ravenow.ui.follower.FollowerNoticeViewModel 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.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.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.GroupChatListScreen
import com.aiosman.ravenow.ui.index.tabs.message.tab.GroupChatListViewModel
import com.aiosman.ravenow.ui.like.LikeNoticeViewModel import com.aiosman.ravenow.ui.like.LikeNoticeViewModel
import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
@@ -77,6 +83,10 @@ import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class) @OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
@Composable @Composable
fun NotificationsScreen() { fun NotificationsScreen() {
// 计算总未读消息数
val totalUnreadCount = AgentChatListViewModel.totalUnreadCount +
GroupChatListViewModel.totalUnreadCount +
FriendChatListViewModel.totalUnreadCount
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
val navController = LocalNavController.current val navController = LocalNavController.current
val systemUiController = rememberSystemUiController() val systemUiController = rememberSystemUiController()
@@ -87,6 +97,12 @@ fun NotificationsScreen() {
MessageListViewModel.viewModelScope.launch { MessageListViewModel.viewModelScope.launch {
MessageListViewModel.initData(context, force = true, loadChat = AppState.enableChat) MessageListViewModel.initData(context, force = true, loadChat = AppState.enableChat)
} }
// 刷新群聊列表以更新未读消息数
GroupChatListViewModel.refreshPager(context = context)
// 刷新智能体列表以更新未读消息数
AgentChatListViewModel.refreshPager(context = context)
// 刷新朋友列表以更新未读消息数
FriendChatListViewModel.refreshPager(context = context)
}) })
val navigationBarPaddings = val navigationBarPaddings =
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp
@@ -94,6 +110,12 @@ fun NotificationsScreen() {
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
systemUiController.setNavigationBarColor(Color.Transparent) systemUiController.setNavigationBarColor(Color.Transparent)
MessageListViewModel.initData(context, loadChat = AppState.enableChat) MessageListViewModel.initData(context, loadChat = AppState.enableChat)
// 初始化群聊列表以获取未读消息数
GroupChatListViewModel.refreshPager(context = context)
// 初始化智能体列表以获取未读消息数
AgentChatListViewModel.refreshPager(context = context)
// 初始化朋友列表以获取未读消息数
FriendChatListViewModel.refreshPager(context = context)
} }
Column( Column(
modifier = Modifier modifier = Modifier
@@ -202,6 +224,7 @@ fun NotificationsScreen() {
horizontalArrangement = Arrangement.Start, horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.Bottom verticalAlignment = Alignment.Bottom
) { ) {
Box {
TabItem( TabItem(
text = stringResource(R.string.chat_ai), text = stringResource(R.string.chat_ai),
isSelected = pagerState.currentPage == 0, isSelected = pagerState.currentPage == 0,
@@ -209,10 +232,25 @@ fun NotificationsScreen() {
scope.launch { scope.launch {
pagerState.animateScrollToPage(0) 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() TabSpacer()
Box {
TabItem( TabItem(
text = stringResource(R.string.chat_group), text = stringResource(R.string.chat_group),
isSelected = pagerState.currentPage == 1, isSelected = pagerState.currentPage == 1,
@@ -222,7 +260,23 @@ fun NotificationsScreen() {
} }
} }
) )
// 群聊未读消息红点
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() TabSpacer()
Box {
TabItem( TabItem(
text = stringResource(R.string.chat_friend), text = stringResource(R.string.chat_friend),
isSelected = pagerState.currentPage == 2, isSelected = pagerState.currentPage == 2,
@@ -232,6 +286,21 @@ fun NotificationsScreen() {
} }
} }
) )
// 朋友未读消息红点
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( HorizontalPager(
state = pagerState, state = pagerState,

View File

@@ -1,5 +1,6 @@
package com.aiosman.ravenow.ui.index.tabs.message.tab package com.aiosman.ravenow.ui.index.tabs.message.tab
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@@ -29,7 +30,9 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp 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.LocalAppTheme
import com.aiosman.ravenow.LocalNavController import com.aiosman.ravenow.LocalNavController
import com.aiosman.ravenow.R import com.aiosman.ravenow.R
import com.aiosman.ravenow.ui.NavigationRoute
import com.aiosman.ravenow.ui.composables.CustomAsyncImage import com.aiosman.ravenow.ui.composables.CustomAsyncImage
import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.aiosman.ravenow.ui.modifiers.noRippleClickable
@@ -82,8 +86,16 @@ fun AgentChatListScreen() {
.fillMaxSize() .fillMaxSize()
.padding(16.dp), .padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally, 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(
text = stringResource(R.string.agent_chat_empty_title), text = stringResource(R.string.agent_chat_empty_title),
color = AppColors.text, color = AppColors.text,
@@ -93,7 +105,7 @@ fun AgentChatListScreen() {
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Text( Text(
text = stringResource(R.string.agent_chat_empty_subtitle), text = stringResource(R.string.agent_chat_empty_subtitle),
color = AppColors.secondaryText, color = AppColors.text,
fontSize = 14.sp fontSize = 14.sp
) )
} }

View File

@@ -92,6 +92,10 @@ object AgentChatListViewModel : ViewModel() {
var currentPage by mutableStateOf(1) var currentPage by mutableStateOf(1)
var error by mutableStateOf<String?>(null) var error by mutableStateOf<String?>(null)
// 计算智能体总未读消息数
val totalUnreadCount: Int
get() = agentChatList.sumOf { it.unreadCount }
private val pageSize = 20 private val pageSize = 20
fun refreshPager(pullRefresh: Boolean = false, context: Context? = null) { fun refreshPager(pullRefresh: Boolean = false, context: Context? = null) {
@@ -200,10 +204,10 @@ object AgentChatListViewModel : ViewModel() {
} }
} }
fun createSingleChat( fun createSingleChat(
openId: String, trtcId: String,
) { ) {
viewModelScope.launch { viewModelScope.launch {
val response = ApiClient.api.createSingleChat(SingleChatRequestBody(generateText = openId)) val response = ApiClient.api.createSingleChat(SingleChatRequestBody(agentTrtcId = trtcId))
} }
} }

View File

@@ -1,5 +1,6 @@
package com.aiosman.ravenow.ui.index.tabs.message.tab package com.aiosman.ravenow.ui.index.tabs.message.tab
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn 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.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
@@ -68,6 +70,15 @@ fun FriendChatListScreen() {
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center 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(
text = stringResource(R.string.friend_chat_empty_title), text = stringResource(R.string.friend_chat_empty_title),
color = AppColors.text, color = AppColors.text,
@@ -77,7 +88,7 @@ fun FriendChatListScreen() {
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Text( Text(
text = stringResource(R.string.friend_chat_empty_subtitle), text = stringResource(R.string.friend_chat_empty_subtitle),
color = AppColors.secondaryText, color = AppColors.text,
fontSize = 14.sp fontSize = 14.sp
) )
} }

View File

@@ -86,6 +86,10 @@ object FriendChatListViewModel : ViewModel() {
var currentPage by mutableStateOf(1) var currentPage by mutableStateOf(1)
var error by mutableStateOf<String?>(null) var error by mutableStateOf<String?>(null)
// 计算朋友总未读消息数
val totalUnreadCount: Int
get() = friendChatList.sumOf { it.unreadCount }
private val pageSize = 20 private val pageSize = 20
fun refreshPager(pullRefresh: Boolean = false, context: Context? = null) { fun refreshPager(pullRefresh: Boolean = false, context: Context? = null) {

View File

@@ -1,6 +1,8 @@
package com.aiosman.ravenow.ui.index.tabs.message.tab package com.aiosman.ravenow.ui.index.tabs.message.tab
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed 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.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
@@ -78,16 +81,29 @@ fun GroupChatListScreen() {
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center 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 = "暂无群聊", text = stringResource(R.string.group_chat_empty_title),
color = AppColors.text, color = AppColors.text,
fontSize = 16.sp, fontSize = 16.sp,
fontWeight = FontWeight.W600 fontWeight = FontWeight.W600
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Text( Text(
text = "您还没有加入任何群聊", text = stringResource(R.string.group_chat_empty_subtitle),
color = AppColors.secondaryText, color = AppColors.text,
fontSize = 14.sp fontSize = 14.sp
) )
} }

View File

@@ -103,6 +103,10 @@ object GroupChatListViewModel : ViewModel() {
var currentPage by mutableStateOf(1) var currentPage by mutableStateOf(1)
var error by mutableStateOf<String?>(null) var error by mutableStateOf<String?>(null)
// 计算群聊总未读消息数
val totalUnreadCount: Int
get() = groupChatList.sumOf { it.unreadCount }
private val pageSize = 20 private val pageSize = 20
// 消息监听器 // 消息监听器

View File

@@ -270,6 +270,15 @@ fun Explore() {
state = pagerState, state = pagerState,
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) { page -> ) { page ->
// 计算当前页面的偏移量
val pageOffset = (
(pagerState.currentPage - page) + pagerState
.currentPageOffsetFraction
).coerceIn(-1f, 1f)
// 根据偏移量计算缩放比例
val scale = 1f - (0.1f * kotlin.math.abs(pageOffset))
AgentPage( AgentPage(
agentItems = agentItems.drop(page * itemsPerPage).take(itemsPerPage), agentItems = agentItems.drop(page * itemsPerPage).take(itemsPerPage),
page = page page = page
@@ -653,7 +662,19 @@ fun Explore() {
contentDescription = "创建智能体", contentDescription = "创建智能体",
modifier = Modifier.size(24.dp), modifier = Modifier.size(24.dp),
<<<<<<< HEAD
) )
=======
)
}
Spacer(modifier = Modifier.size(8.dp))
Text(
text = "发布动态",
fontSize = 12.sp,
color = AppColors.text,
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
)
>>>>>>> upstream/main
} }
Spacer(modifier = Modifier.size(8.dp)) Spacer(modifier = Modifier.size(8.dp))
Text( Text(
@@ -767,10 +788,35 @@ fun Explore() {
) { ) {
HorizontalPager( HorizontalPager(
state = pagerState, state = pagerState,
<<<<<<< HEAD
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) { page -> ) { page ->
val bannerItem = bannerItems[page] val bannerItem = bannerItems[page]
BannerCard(bannerItem = bannerItem) BannerCard(bannerItem = bannerItem)
=======
modifier = Modifier.fillMaxSize(),
contentPadding = androidx.compose.foundation.layout.PaddingValues(horizontal = 4.dp),
) { page ->
val bannerItem = bannerItems[page]
// 计算当前页面的偏移量
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
}
)
>>>>>>> upstream/main
} }
} }

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:strokeWidth="1"
android:pathData="M 0 0 L 24 0 L 24 24 L 0 24 Z" />
<path
android:strokeColor="#000000"
android:strokeWidth="2"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M 15 8 L 15.01 8" />
<path
android:strokeColor="#000000"
android:strokeWidth="2"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M 7 4 L 17 4 Q 20 4 20 7 L 20 17 Q 20 20 17 20 L 7 20 Q 4 20 4 17 L 4 7 Q 4 4 7 4 Z" />
<path
android:strokeColor="#000000"
android:strokeWidth="2"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M4,15 L8,11 C8.92820323,10.106836 10.0717968,10.106836 11,11 L16,16" />
<path
android:strokeColor="#000000"
android:strokeWidth="2"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M14,14 L15,13 C15.9282032,12.106836 17.0717968,12.106836 18,13 L20,15" />
</vector>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillType="evenOdd"
android:pathData="M0 0h24v24H0z" />
<path
android:fillType="evenOdd"
android:strokeColor="#000"
android:strokeWidth="2"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M14 8V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h7a2 2 0 0 0 2-2v-2" />
<path
android:fillType="evenOdd"
android:strokeColor="#000"
android:strokeWidth="2"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M7 12h14l-3-3m0 6 3-3" />
</vector>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillType="evenOdd"
android:strokeColor="#000"
android:strokeWidth="2"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M21 16.5H3a2.7 2.7 0 0 0 2.7-2.7V9.3a6.3 6.3 0 0 1 12.6 0v4.5a2.7 2.7 0 0 0 2.7 2.7h0zm-7.443 3.6a1.8 1.8 0 0 1-3.114 0" />
</vector>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group
android:translateX="2"
android:translateY="4">
<path
android:fillType="evenOdd"
android:strokeColor="#000"
android:strokeWidth="2"
android:pathData="M7.273 10.777c4.016 0 7.272 1.791 7.272 4 0 0.706-0.331 1.368-0.914 1.944-0.392 0.179 -0.828 0.278 -1.288 0.278 H2.202a3.1 3.1 0 0 1-1.288-0.278C0.332 16.145 0 15.483 0 14.777c0-2.209 3.256-4 7.273-4z" />
<path
android:fillType="evenOdd"
android:strokeColor="#000"
android:strokeWidth="2"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M16.491 17h1.236c0.5 0 2.273-0.398 2.273-2.223 0-1.49-1.482-2.79-3.68-3.478" />
<path
android:fillType="evenOdd"
android:strokeColor="#000"
android:strokeWidth="2"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M 7.273 0 C 9.28110735038 0 10.909 1.59162771435 10.909 3.555 C 10.909 5.51837228565 9.28110735038 7.11 7.273 7.11 C 5.26489264962 7.11 3.637 5.51837228565 3.637 3.555 C 3.637 1.59162771435 5.26489264962 0 7.273 0 Z" />
<path
android:fillType="evenOdd"
android:strokeColor="#000"
android:strokeWidth="2"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M13.636 0.116 c1.61 0.402 2.735 1.82 2.735 3.444 0 1.624-1.126 3.041-2.735 3.444" />
</group>
</vector>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group
android:translateX="2"
android:translateY="4">
<path
android:fillType="evenOdd"
android:strokeColor="#110C13"
android:strokeWidth="1.778"
android:pathData="M7.273 10.777c4.016 0 7.272 1.791 7.272 4 0 0.706-0.331 1.368-0.914 1.944-0.392 0.179 -0.828 0.278 -1.288 0.278 H2.202a3.1 3.1 0 0 1-1.288-0.278C0.332 16.145 0 15.483 0 14.777c0-2.209 3.256-4 7.273-4z" />
<path
android:fillType="evenOdd"
android:strokeColor="#110C13"
android:strokeWidth="1.778"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M 7.273 0 C 9.28110735038 0 10.909 1.59162771435 10.909 3.555 C 10.909 5.51837228565 9.28110735038 7.11 7.273 7.11 C 5.26489264962 7.11 3.637 5.51837228565 3.637 3.555 C 3.637 1.59162771435 5.26489264962 0 7.273 0 Z" />
</group>
<path
android:fillType="evenOdd"
android:strokeColor="#110C13"
android:strokeWidth="2"
android:strokeLineCap="round"
android:pathData="M16.364 10.945h6M19.364 7.945v6" />
</vector>

View File

@@ -1,5 +1,21 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:alpha="0.77" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> <?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
<path android:fillColor="@android:color/white" android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zM12,6.5c2.49,0 4,2.02 4,4.5v0.1l2,2L18,11c0,-3.07 -1.63,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68c-0.24,0.06 -0.47,0.15 -0.69,0.23l1.64,1.64c0.18,-0.02 0.36,-0.05 0.55,-0.05zM5.41,3.35L4,4.76l2.81,2.81C6.29,8.57 6,9.74 6,11v5l-2,2v1h14.24l1.74,1.74 1.41,-1.41L5.41,3.35zM16,17L8,17v-6c0,-0.68 0.12,-1.32 0.34,-1.9L16,16.76L16,17z"/> android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillType="evenOdd"
android:strokeColor="#000"
android:strokeWidth="2"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M16.06 16.5H3a2.7 2.7 0 0 0 2.7-2.7V9.3c0-0.913 0.194 -1.78 0.543 -2.563m2.564-2.87A6.3 6.3 0 0 1 18.3 9.3v4.5m-4.743 6.3a1.8 1.8 0 0 1-3.114 0" />
<path
android:strokeColor="#000"
android:strokeWidth="2"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M3 3 18.75 18.75" />
</vector> </vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -147,8 +147,8 @@
<string name="chat_friend">朋友</string> <string name="chat_friend">朋友</string>
<string name="agent_chat_list_title">智能体聊天</string> <string name="agent_chat_list_title">智能体聊天</string>
<string name="agent_chat_empty_title">暂无智能体聊天</string> <string name="agent_chat_empty_title">AI 们在等你开启第一句对话</string>
<string name="agent_chat_empty_subtitle">开始与智能体对话</string> <string name="agent_chat_empty_subtitle">去首页探索一下,主动发起一场对话</string>
<string name="agent_chat_me_prefix">我: </string> <string name="agent_chat_me_prefix">我: </string>
<string name="agent_chat_image">[图片]</string> <string name="agent_chat_image">[图片]</string>
<string name="agent_chat_voice">[语音]</string> <string name="agent_chat_voice">[语音]</string>
@@ -159,6 +159,9 @@
<string name="agent_chat_load_more_failed">加载更多失败</string> <string name="agent_chat_load_more_failed">加载更多失败</string>
<string name="agent_chat_user_info_failed">获取用户信息失败: %s</string> <string name="agent_chat_user_info_failed">获取用户信息失败: %s</string>
<string name="group_chat_empty_title">没有群聊消息的宇宙太安静了</string>
<string name="group_chat_empty_subtitle">在首页探索感兴趣的主题房间</string>
<string name="friend_chat_empty_title">暂无朋友聊天</string> <string name="friend_chat_empty_title">暂无朋友聊天</string>
<string name="friend_chat_empty_subtitle">开始与朋友对话吧</string> <string name="friend_chat_empty_subtitle">开始与朋友对话吧</string>
<string name="friend_chat_me_prefix">我: </string> <string name="friend_chat_me_prefix">我: </string>
@@ -167,5 +170,9 @@
<string name="quick_create">一键创建</string> <string name="quick_create">一键创建</string>
<string name="group_name">群聊名称</string> <string name="group_name">群聊名称</string>
<string name="search_placeholder">搜索</string> <string name="search_placeholder">搜索</string>
<string name="group_info_add_other">添加其他人</string>
<string name="group_info_notice_setting">通知</string>
<string name="group_info_exit">退出</string>
<string name="group_info_edit">编辑资料</string>
</resources> </resources>

View File

@@ -155,6 +155,8 @@
<string name="agent_chat_load_failed">加载失败</string> <string name="agent_chat_load_failed">加载失败</string>
<string name="agent_chat_load_more_failed">加载更多失败</string> <string name="agent_chat_load_more_failed">加载更多失败</string>
<string name="agent_chat_user_info_failed">获取用户信息失败: %s</string> <string name="agent_chat_user_info_failed">获取用户信息失败: %s</string>
<string name="group_chat_empty_title">没有群聊消息的宇宙太安静了</string>
<string name="group_chat_empty_subtitle">在首页探索感兴趣的主题房间</string>
<string name="friend_chat_empty_title">暂无朋友聊天</string> <string name="friend_chat_empty_title">暂无朋友聊天</string>
<string name="friend_chat_empty_subtitle">开始与朋友对话吧</string> <string name="friend_chat_empty_subtitle">开始与朋友对话吧</string>
<string name="friend_chat_me_prefix">我: </string> <string name="friend_chat_me_prefix">我: </string>
@@ -163,5 +165,9 @@
<string name="quick_create">Quick Create</string> <string name="quick_create">Quick Create</string>
<string name="group_name">Group Name</string> <string name="group_name">Group Name</string>
<string name="search_placeholder">Search</string> <string name="search_placeholder">Search</string>
<string name="group_info_add_other">添加其他人</string>
<string name="group_info_notice_setting">通知</string>
<string name="group_info_exit">退出</string>
<string name="group_info_edit">编辑资料</string>
</resources> </resources>