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