UI调整,群聊开发

This commit is contained in:
weber
2025-08-20 19:19:14 +08:00
parent 791b24b2fb
commit 8f8c2ff2e9
27 changed files with 709 additions and 513 deletions

View File

@@ -83,6 +83,7 @@ import com.aiosman.ravenow.ui.composables.StatusBarSpacer
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import com.tencent.imsdk.v2.V2TIMMessage
import kotlinx.coroutines.launch
import java.util.UUID
@Composable
@@ -280,7 +281,7 @@ fun ChatAiScreen(userId: String) {
verticalArrangement = Arrangement.Top
) {
val chatList = groupMessagesByTime(viewModel.getDisplayChatList(), viewModel)
items(chatList.size, key = { index -> chatList[index].msgId }) { index ->
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()

View File

@@ -68,11 +68,22 @@ class ChatAiViewModel(
textMessageListener = object : V2TIMAdvancedMsgListener() {
override fun onRecvNewMessage(msg: V2TIMMessage?) {
super.onRecvNewMessage(msg)
msg?.let {
val chatItem = ChatItem.convertToChatItem(msg, context, avatar = userProfile?.avatar)
chatItem?.let {
chatData = listOf(it) + chatData
goToNew = true
msg?.let { message ->
// 只处理当前聊天对象的消息
val currentChatUserId = userProfile?.trtcUserId
val currentUserId = com.aiosman.ravenow.AppState.profile?.trtcUserId
if (currentChatUserId != null && currentUserId != null) {
// 检查消息是否来自当前聊天对象,且不是自己发送的消息
if ((message.userID == currentChatUserId || message.userID == currentUserId) &&
message.sender != currentUserId) {
val chatItem = ChatItem.convertToChatItem(message, context, avatar = userProfile?.avatar)
chatItem?.let {
chatData = listOf(it) + chatData
goToNew = true
android.util.Log.i("ChatAiViewModel", "收到来自 ${message.sender} 的消息更新AI聊天列表")
}
}
}
}
}
@@ -260,7 +271,7 @@ class ChatAiViewModel(
message: String,
) {
viewModelScope.launch {
val response = ApiClient.api.sendChatAiMessage(SendChatAiRequestBody(fromTrtcUserId = fromTrtcUserId,toTrtcUserId = toTrtcUserId,message = message,skipTrtc = true))
val response = ApiClient.api.sendChatAiMessage(SendChatAiRequestBody(fromTrtcUserId = fromTrtcUserId,toTrtcUserId = toTrtcUserId,message = message))
}
}

View File

@@ -83,6 +83,7 @@ import com.aiosman.ravenow.ui.composables.StatusBarSpacer
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import com.tencent.imsdk.v2.V2TIMMessage
import kotlinx.coroutines.launch
import java.util.UUID
@Composable
@@ -280,7 +281,7 @@ fun ChatScreen(userId: String) {
verticalArrangement = Arrangement.Top
) {
val chatList = groupMessagesByTime(viewModel.getDisplayChatList(), viewModel)
items(chatList.size, key = { index -> chatList[index].msgId }) { index ->
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()

View File

@@ -65,11 +65,22 @@ class ChatViewModel(
textMessageListener = object : V2TIMAdvancedMsgListener() {
override fun onRecvNewMessage(msg: V2TIMMessage?) {
super.onRecvNewMessage(msg)
msg?.let {
val chatItem = ChatItem.convertToChatItem(msg, context, avatar = userProfile?.avatar)
chatItem?.let {
chatData = listOf(it) + chatData
goToNew = true
msg?.let { message ->
// 只处理当前聊天对象的消息
val currentChatUserId = userProfile?.trtcUserId
val currentUserId = com.aiosman.ravenow.AppState.profile?.trtcUserId
if (currentChatUserId != null && currentUserId != null) {
// 检查消息是否来自当前聊天对象,且不是自己发送的消息
if ((message.userID == currentChatUserId || message.userID == currentUserId) &&
message.sender != currentUserId) {
val chatItem = ChatItem.convertToChatItem(message, context, avatar = userProfile?.avatar)
chatItem?.let {
chatData = listOf(it) + chatData
goToNew = true
android.util.Log.i("ChatViewModel", "收到来自 ${message.sender} 的消息,更新聊天列表")
}
}
}
}
}

View File

@@ -65,6 +65,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.ImeAction
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 androidx.lifecycle.ViewModel
@@ -83,9 +84,10 @@ import com.aiosman.ravenow.ui.composables.StatusBarSpacer
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import com.tencent.imsdk.v2.V2TIMMessage
import kotlinx.coroutines.launch
import java.util.UUID
@Composable
fun GroupChatScreen(groupId: String) {
fun GroupChatScreen(groupId: String,name: String,avatar: String,) {
var isMenuExpanded by remember { mutableStateOf(false) }
val navController = LocalNavController.current
val context = LocalNavController.current.context
@@ -96,7 +98,7 @@ fun GroupChatScreen(groupId: String) {
key = "GroupChatViewModel_$groupId",
factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return GroupChatViewModel(groupId) as T
return GroupChatViewModel(groupId,name,avatar) as T
}
}
)
@@ -167,7 +169,7 @@ fun GroupChatScreen(groupId: String) {
Image(
painter = painterResource(R.drawable.rider_pro_back_icon),
modifier = Modifier
.size(28.dp)
.size(24.dp)
.noRippleClickable {
navController.navigateUp()
},
@@ -180,7 +182,7 @@ fun GroupChatScreen(groupId: String) {
CustomAsyncImage(
imageUrl = viewModel.groupAvatar,
modifier = Modifier
.size(40.dp)
.size(32.dp)
.clip(RoundedCornerShape(8.dp)),
contentDescription = "群聊头像"
)
@@ -193,12 +195,16 @@ fun GroupChatScreen(groupId: String) {
contentAlignment = Alignment.Center
) {
Text(
text = viewModel.groupName.take(1),
text = viewModel.groupName,
style = TextStyle(
color = AppColors.text,
fontSize = 16.sp,
fontWeight = androidx.compose.ui.text.font.FontWeight.Bold
)
fontSize = 18.sp,
fontWeight = androidx.compose.ui.text.font.FontWeight.W700
),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
@@ -213,13 +219,7 @@ fun GroupChatScreen(groupId: String) {
fontWeight = androidx.compose.ui.text.font.FontWeight.Bold
)
)
Text(
text = "${viewModel.memberCount}",
style = TextStyle(
color = AppColors.secondaryText,
fontSize = 14.sp
)
)
}
Spacer(modifier = Modifier.weight(1f))
@@ -304,7 +304,7 @@ fun GroupChatScreen(groupId: String) {
verticalArrangement = Arrangement.Top
) {
val chatList = groupMessagesByTime(viewModel.getDisplayChatList(), viewModel)
items(chatList.size, key = { index -> chatList[index].msgId }) { index ->
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()
@@ -369,7 +369,7 @@ fun GroupChatSelfItem(item: ChatItem) {
Column(
horizontalAlignment = androidx.compose.ui.Alignment.End,
) {
Text(
/* Text(
text = item.nickname,
style = TextStyle(
color = Color.Gray,
@@ -377,15 +377,15 @@ fun GroupChatSelfItem(item: ChatItem) {
),
modifier = Modifier.padding(bottom = 2.dp)
)
*/
Box(
modifier = Modifier
.widthIn(
min = 20.dp,
max = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 250.dp else 150.dp)
)
.clip(RoundedCornerShape(8.dp))
.background(Color(0xFF000000))
.clip(RoundedCornerShape(20.dp))
.background(Color(0xFF6246FF))
.padding(
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)
@@ -398,7 +398,7 @@ fun GroupChatSelfItem(item: ChatItem) {
text = item.message,
style = TextStyle(
color = Color.White,
fontSize = 16.sp,
fontSize = 14.sp,
),
textAlign = TextAlign.Start
)
@@ -417,25 +417,25 @@ fun GroupChatSelfItem(item: ChatItem) {
text = "不支持的消息类型",
style = TextStyle(
color = Color.White,
fontSize = 16.sp,
fontSize = 14.sp,
)
)
}
}
}
}
Spacer(modifier = Modifier.width(12.dp))
/*Spacer(modifier = Modifier.width(12.dp))
Box(
modifier = Modifier
.size(40.dp)
.clip(RoundedCornerShape(40.dp))
.size(24.dp)
.clip(RoundedCornerShape(24.dp))
) {
CustomAsyncImage(
imageUrl = item.avatar,
modifier = Modifier.fillMaxSize(),
contentDescription = "avatar"
)
}
}*/
}
}
}
@@ -455,11 +455,11 @@ fun GroupChatOtherItem(item: ChatItem) {
) {
Box(
modifier = Modifier
.size(40.dp)
.clip(RoundedCornerShape(40.dp))
.size(24.dp)
.clip(RoundedCornerShape(24.dp))
) {
CustomAsyncImage(
imageUrl = item.avatar,
imageUrl = item.avatar.replace("storage/avatars/", "/avatar/"),
modifier = Modifier.fillMaxSize(),
contentDescription = "avatar"
)
@@ -481,8 +481,8 @@ fun GroupChatOtherItem(item: ChatItem) {
min = 20.dp,
max = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 250.dp else 150.dp)
)
.clip(RoundedCornerShape(8.dp))
.background(AppColors.background)
.clip(RoundedCornerShape(20.dp))
.background(AppColors.bubbleBackground)
.padding(
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)
@@ -495,7 +495,7 @@ fun GroupChatOtherItem(item: ChatItem) {
text = item.message,
style = TextStyle(
color = AppColors.text,
fontSize = 16.sp,
fontSize = 14.sp,
),
textAlign = TextAlign.Start
)
@@ -528,10 +528,47 @@ fun GroupChatOtherItem(item: ChatItem) {
@Composable
fun GroupChatItem(item: ChatItem, currentUserId: String) {
val isCurrentUser = item.userId == currentUserId
if (isCurrentUser) {
GroupChatSelfItem(item)
} else {
GroupChatOtherItem(item)
// 管理员消息显示特殊布局
if (item.userId == "administrator") {
GroupChatAdminItem(item)
return
}
// 根据是否是当前用户显示不同样式
when (item.userId) {
currentUserId -> GroupChatSelfItem(item)
else -> GroupChatOtherItem(item)
}
}
@Composable
fun GroupChatAdminItem(item: ChatItem) {
val AppColors = LocalAppTheme.current
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 50.dp, vertical = 8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(8.dp))
.padding(vertical = 8.dp, horizontal = 16.dp),
contentAlignment = Alignment.Center
) {
Text(
text = item.message,
style = TextStyle(
color = AppColors.secondaryText,
fontSize = 12.sp,
textAlign = TextAlign.Center
),
maxLines = Int.MAX_VALUE
)
}
}
}

View File

@@ -15,6 +15,10 @@ import com.aiosman.ravenow.data.AccountService
import com.aiosman.ravenow.data.AccountServiceImpl
import com.aiosman.ravenow.data.UserService
import com.aiosman.ravenow.data.UserServiceImpl
import com.aiosman.ravenow.data.api.ApiClient
import com.aiosman.ravenow.data.api.GroupChatRequestBody
import com.aiosman.ravenow.data.api.SendChatAiRequestBody
import com.aiosman.ravenow.data.api.SingleChatRequestBody
import com.aiosman.ravenow.entity.AccountProfileEntity
import com.aiosman.ravenow.entity.ChatItem
import com.aiosman.ravenow.entity.ChatNotification
@@ -31,6 +35,8 @@ import java.io.InputStream
class GroupChatViewModel(
val groupId: String,
val name: String,
val avatar: String,
) : ViewModel() {
var chatData by mutableStateOf<List<ChatItem>>(emptyList())
var groupInfo by mutableStateOf<GroupInfo?>(null)
@@ -77,8 +83,8 @@ class GroupChatViewModel(
// 简化群组信息获取,使用默认信息
groupInfo = GroupInfo(
groupId = groupId,
groupName = "群聊 $groupId",
groupAvatar = "",
groupName = name,
groupAvatar = avatar,
memberCount = 0,
ownerId = ""
)
@@ -156,8 +162,8 @@ class GroupChatViewModel(
val v2TIMMessage = V2TIMManager.getMessageManager().createTextMessage(message)
V2TIMManager.getMessageManager().sendMessage(
v2TIMMessage,
groupId,
null,
groupId,
V2TIMMessage.V2TIM_PRIORITY_NORMAL,
false,
null,
@@ -167,6 +173,7 @@ class GroupChatViewModel(
Log.e("GroupChatViewModel", "发送群聊消息失败: $p1")
}
override fun onSuccess(p0: V2TIMMessage?) {
sendChatAiMessage(message = message, trtcGroupId = groupId)
val chatItem = ChatItem.convertToChatItem(p0!!, context, avatar = myProfile?.avatar)
chatItem?.let {
chatData = listOf(it) + chatData
@@ -177,6 +184,18 @@ class GroupChatViewModel(
)
}
fun sendChatAiMessage(
trtcGroupId: String,
message: String,
) {
viewModelScope.launch {
val response = ApiClient.api.sendChatAiMessage(SendChatAiRequestBody(trtcGroupId = trtcGroupId,message = message))
}
}
fun sendImageMessage(imageUri: Uri, context: Context) {
val tempFile = createTempFile(context, imageUri)
val imagePath = tempFile?.path