This commit is contained in:
weber
2025-08-25 10:49:00 +08:00
parent 4be63f3428
commit 77033854f0
21 changed files with 721 additions and 339 deletions

View File

@@ -6,6 +6,7 @@ import com.aiosman.ravenow.entity.AgentEntity
import com.aiosman.ravenow.entity.CreatorEntity import com.aiosman.ravenow.entity.CreatorEntity
import com.aiosman.ravenow.entity.ProfileEntity import com.aiosman.ravenow.entity.ProfileEntity
import com.aiosman.ravenow.entity.RoomEntity import com.aiosman.ravenow.entity.RoomEntity
import com.aiosman.ravenow.entity.UsersEntity
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
data class Room( data class Room(
@@ -38,7 +39,9 @@ data class Room(
@SerializedName("canJoin") @SerializedName("canJoin")
val canJoin: Boolean, val canJoin: Boolean,
@SerializedName("canJoinCode") @SerializedName("canJoinCode")
val canJoinCode: Int val canJoinCode: Int,
@SerializedName("users")
val users: List<Users>
) { ) {
fun toRoomtEntity(): RoomEntity { fun toRoomtEntity(): RoomEntity {
@@ -58,6 +61,7 @@ data class Room(
maxMemberLimit = maxMemberLimit, maxMemberLimit = maxMemberLimit,
canJoin = canJoin, canJoin = canJoin,
canJoinCode = canJoinCode, canJoinCode = canJoinCode,
users = users.map { it.toUsersEntity() }
) )
} }
} }
@@ -83,6 +87,25 @@ data class Creator(
} }
} }
data class Users(
@SerializedName("id")
val id: Int,
@SerializedName("userId")
val userId: String,
@SerializedName("trtcUserId")
val trtcUserId: String,
@SerializedName("profile")
val profile: Profile
){
fun toUsersEntity(): UsersEntity {
return UsersEntity(
id = id,
userId = userId,
profile = profile.toProfileEntity()
)
}
}

View File

@@ -76,6 +76,11 @@ data class CreateGroupChatRequestBody(
val promptIds: List<String>, val promptIds: List<String>,
) )
data class JoinGroupChatRequestBody(
@SerializedName("trtcId")
val trtcId: String,
)
data class LoginUserRequestBody( data class LoginUserRequestBody(
@SerializedName("username") @SerializedName("username")
val username: String? = null, val username: String? = null,
@@ -567,6 +572,9 @@ interface RaveNowAPI {
suspend fun getRoomDetail(@Query("trtcId") trtcId: String, suspend fun getRoomDetail(@Query("trtcId") trtcId: String,
): Response<DataContainer<Room>> ): Response<DataContainer<Room>>
@POST("outside/rooms/join")
suspend fun joinRoom(@Body body: JoinGroupChatRequestBody,
): Response<DataContainer<Room>>

View File

@@ -12,4 +12,5 @@ data class GroupInfo(
val groupName: String, val groupName: String,
val groupAvatar: String, val groupAvatar: String,
val memberCount: Int, val memberCount: Int,
val isCreator: Boolean = false,
) )

View File

@@ -28,6 +28,7 @@ data class RoomEntity(
val maxMemberLimit: Int, val maxMemberLimit: Int,
val canJoin: Boolean, val canJoin: Boolean,
val canJoinCode: Int, val canJoinCode: Int,
val users: List<UsersEntity>,
) )
data class CreatorEntity( data class CreatorEntity(
@@ -37,6 +38,12 @@ data class CreatorEntity(
val profile: ProfileEntity val profile: ProfileEntity
) )
data class UsersEntity(
val id: Int,
val userId: String,
val profile: ProfileEntity
)
data class ProfileEntity( data class ProfileEntity(
val id: Int, val id: Int,
val username: String, val username: String,

View File

@@ -35,7 +35,6 @@ 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
@@ -48,6 +47,7 @@ import com.aiosman.ravenow.ui.follower.FollowingListScreen
import com.aiosman.ravenow.ui.gallery.OfficialGalleryScreen import com.aiosman.ravenow.ui.gallery.OfficialGalleryScreen
import com.aiosman.ravenow.ui.gallery.OfficialPhotographerScreen import com.aiosman.ravenow.ui.gallery.OfficialPhotographerScreen
import com.aiosman.ravenow.ui.gallery.ProfileTimelineScreen import com.aiosman.ravenow.ui.gallery.ProfileTimelineScreen
import com.aiosman.ravenow.ui.group.GroupChatInfoScreen
import com.aiosman.ravenow.ui.index.IndexScreen import com.aiosman.ravenow.ui.index.IndexScreen
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.search.SearchScreen import com.aiosman.ravenow.ui.index.tabs.search.SearchScreen
@@ -97,13 +97,13 @@ 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")
data object AboutScreen : NavigationRoute("AboutScreen") data object AboutScreen : NavigationRoute("AboutScreen")
data object AddAgent : NavigationRoute("AddAgent") data object AddAgent : NavigationRoute("AddAgent")
data object CreateGroupChat : NavigationRoute("CreateGroupChat") data object CreateGroupChat : NavigationRoute("CreateGroupChat")
data object GroupInfo : NavigationRoute("GroupInfo/{id}")
} }
@@ -406,19 +406,6 @@ 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(
@@ -460,6 +447,20 @@ fun NavigationController(
CreateGroupChatScreen() CreateGroupChatScreen()
} }
composable(
route = NavigationRoute.GroupInfo.route,
arguments = listOf(navArgument("id") { type = NavType.StringType })
) {
val encodedId = it.arguments?.getString("id")
val decodedId = encodedId?.let { java.net.URLDecoder.decode(it, "UTF-8") }
CompositionLocalProvider(
LocalAnimatedContentScope provides this,
) {
GroupChatInfoScreen(decodedId?:"")
}
}
} }
} }
@@ -531,15 +532,17 @@ fun NavHostController.navigateToGroupChat(id: String,name:String,avatar:String)
fun NavHostController.navigateToGroupInfo(id: String) { fun NavHostController.navigateToGroupInfo(id: String) {
val encodedId = java.net.URLEncoder.encode(id, "UTF-8") val encodedId = java.net.URLEncoder.encode(id, "UTF-8")
navigate( navigate(
route = NavigationRoute.GroupChatInfo.route route = NavigationRoute.ChatGroup.route
.replace("{groupId}", encodedId) .replace("{id}", encodedId)
) )
} }
fun NavHostController.goTo( fun NavHostController.goTo(
route: NavigationRoute route: NavigationRoute
) { ) {

View File

@@ -10,6 +10,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController
import com.aiosman.ravenow.ChatState import com.aiosman.ravenow.ChatState
import com.aiosman.ravenow.data.AccountService import com.aiosman.ravenow.data.AccountService
import com.aiosman.ravenow.data.AccountServiceImpl import com.aiosman.ravenow.data.AccountServiceImpl
@@ -21,8 +22,10 @@ import com.aiosman.ravenow.data.api.SingleChatRequestBody
import com.aiosman.ravenow.entity.AccountProfileEntity import com.aiosman.ravenow.entity.AccountProfileEntity
import com.aiosman.ravenow.entity.ChatItem import com.aiosman.ravenow.entity.ChatItem
import com.aiosman.ravenow.entity.ChatNotification import com.aiosman.ravenow.entity.ChatNotification
import com.aiosman.ravenow.ui.navigateToChatAi
import com.tencent.imsdk.v2.V2TIMAdvancedMsgListener import com.tencent.imsdk.v2.V2TIMAdvancedMsgListener
import com.tencent.imsdk.v2.V2TIMCallback import com.tencent.imsdk.v2.V2TIMCallback
import com.tencent.imsdk.v2.V2TIMConversationOperationResult
import com.tencent.imsdk.v2.V2TIMManager import com.tencent.imsdk.v2.V2TIMManager
import com.tencent.imsdk.v2.V2TIMMessage import com.tencent.imsdk.v2.V2TIMMessage
import com.tencent.imsdk.v2.V2TIMSendCallback import com.tencent.imsdk.v2.V2TIMSendCallback
@@ -162,6 +165,7 @@ class ChatAiViewModel(
override fun onSuccess(p0: V2TIMMessage?) { override fun onSuccess(p0: V2TIMMessage?) {
Log.d("ChatViewModel", "send message success") Log.d("ChatViewModel", "send message success")
sendChatAiMessage(myProfile?.trtcUserId!!,userProfile?.trtcUserId!!, message) sendChatAiMessage(myProfile?.trtcUserId!!,userProfile?.trtcUserId!!, message)
createGroup2ChatAi(userProfile?.trtcUserId!!,"ai_group")
val chatItem = ChatItem.convertToChatItem(p0!!, context, avatar = myProfile?.avatar) val chatItem = ChatItem.convertToChatItem(p0!!, context, avatar = myProfile?.avatar)
chatItem?.let { chatItem?.let {
chatData = listOf(it) + chatData chatData = listOf(it) + chatData
@@ -171,6 +175,27 @@ class ChatAiViewModel(
} }
) )
} }
fun createGroup2ChatAi(
trtcUserId: String,
groupName: String,
) {
val conversationIDList = listOf("c2c_${trtcUserId}")
V2TIMManager.getConversationManager().createConversationGroup(
groupName,
conversationIDList,
object : V2TIMValueCallback<List<V2TIMConversationOperationResult>> {
override fun onSuccess(v2TIMConversationOperationResults: List<V2TIMConversationOperationResult>) {
// 创建会话分组成功
}
override fun onError(code: Int, desc: String) {
// 创建会话分组失败
}
}
)
}
fun sendImageMessage(imageUri: Uri, context: Context) { fun sendImageMessage(imageUri: Uri, context: Context) {
val tempFile = createTempFile(context, imageUri) val tempFile = createTempFile(context, imageUri)

View File

@@ -196,14 +196,16 @@ fun ChatScreen(userId: String) {
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
Text( Text(
text = viewModel.userProfile?.nickName ?: "", text = viewModel.userProfile?.nickName ?: "",
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f)
,
maxLines = 1,
style = TextStyle( style = TextStyle(
color = AppColors.text, color = AppColors.text,
fontSize = 18.sp, fontSize = 18.sp,
fontWeight = androidx.compose.ui.text.font.FontWeight.Bold fontWeight = androidx.compose.ui.text.font.FontWeight.Bold
) )
) )
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.width(8.dp))
Box { Box {
Image( Image(
painter = painterResource(R.drawable.rider_pro_more_horizon), painter = painterResource(R.drawable.rider_pro_more_horizon),

View File

@@ -84,6 +84,7 @@ import com.aiosman.ravenow.ui.composables.DropdownMenu
import com.aiosman.ravenow.ui.composables.MenuItem import com.aiosman.ravenow.ui.composables.MenuItem
import com.aiosman.ravenow.ui.composables.StatusBarSpacer import com.aiosman.ravenow.ui.composables.StatusBarSpacer
import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import com.aiosman.ravenow.ui.navigateToGroupInfo
import com.tencent.imsdk.v2.V2TIMMessage import com.tencent.imsdk.v2.V2TIMMessage
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.UUID import java.util.UUID
@@ -221,7 +222,6 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) {
fontWeight = androidx.compose.ui.text.font.FontWeight.Bold fontWeight = androidx.compose.ui.text.font.FontWeight.Bold
) )
) )
} }
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
@@ -231,7 +231,7 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) {
modifier = Modifier modifier = Modifier
.size(28.dp) .size(28.dp)
.noRippleClickable { .noRippleClickable {
isMenuExpanded = true navController.navigateToGroupInfo(groupId)
}, },
contentDescription = null, contentDescription = null,
colorFilter = ColorFilter.tint(AppColors.text) colorFilter = ColorFilter.tint(AppColors.text)

View File

@@ -23,6 +23,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.aiosman.ravenow.LocalAppTheme import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.LocalNavController import com.aiosman.ravenow.LocalNavController
@@ -30,6 +31,7 @@ import com.aiosman.ravenow.R
import com.aiosman.ravenow.ui.composables.CustomAsyncImage import com.aiosman.ravenow.ui.composables.CustomAsyncImage
import com.aiosman.ravenow.ui.composables.StatusBarSpacer import com.aiosman.ravenow.ui.composables.StatusBarSpacer
import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import kotlinx.coroutines.launch
@Composable @Composable
fun GroupChatInfoScreen(groupId: String) { fun GroupChatInfoScreen(groupId: String) {
@@ -79,7 +81,7 @@ fun GroupChatInfoScreen(groupId: String) {
Text( Text(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Start, textAlign = TextAlign.Start,
text = "群聊信息", text = stringResource(R.string.group_info),
style = androidx.compose.ui.text.TextStyle( style = androidx.compose.ui.text.TextStyle(
color = AppColors.text, color = AppColors.text,
fontSize = 17.sp, fontSize = 17.sp,
@@ -119,7 +121,7 @@ fun GroupChatInfoScreen(groupId: String) {
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Text( Text(
text = viewModel.groupInfo?.groupName?.firstOrNull()?.toString() ?: "", text = viewModel.groupInfo?.groupName?.firstOrNull()?.toString() ?: "",
style = androidx.compose.ui.text.TextStyle( style = androidx.compose.ui.text.TextStyle(
color = AppColors.text, color = AppColors.text,
fontSize = 24.sp, fontSize = 24.sp,
@@ -184,11 +186,13 @@ fun GroupChatInfoScreen(groupId: String) {
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.noRippleClickable { modifier = Modifier.noRippleClickable {
/* if (viewModel.notificationStrategy == "mute") { viewModel.viewModelScope.launch {
viewModel.updateNotificationStrategy("active") if (viewModel.notificationStrategy == "mute") {
} else { viewModel.updateNotificationStrategy("active")
viewModel.updateNotificationStrategy("mute") } else {
}*/ viewModel.updateNotificationStrategy("mute")
}
}
} }
) { ) {
Box( Box(

View File

@@ -11,6 +11,7 @@ import com.aiosman.ravenow.data.api.ApiClient
import com.aiosman.ravenow.entity.ChatNotification import com.aiosman.ravenow.entity.ChatNotification
import com.aiosman.ravenow.entity.GroupInfo import com.aiosman.ravenow.entity.GroupInfo
import com.aiosman.ravenow.entity.GroupMember import com.aiosman.ravenow.entity.GroupMember
import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class GroupChatInfoViewModel( class GroupChatInfoViewModel(
@@ -56,9 +57,10 @@ class GroupChatInfoViewModel(
) )
"${ApiClient.RETROFIT_URL+"group/avatar?groupIdBase64="}${groupIdBase64}"+"&token="+"${AppStore.token}" "${ApiClient.RETROFIT_URL+"group/avatar?groupIdBase64="}${groupIdBase64}"+"&token="+"${AppStore.token}"
} else { } else {
"${ApiClient.BASE_API_URL+"/outside/rooms/avatar/"}${it.avatar}"+"?token="+"${AppStore.token}" "${ApiClient.BASE_API_URL+"/outside"}${it.avatar}"+"?token="+"${AppStore.token}"
}, },
memberCount = room.userCount, memberCount = room.userCount,
isCreator = room.creator.userId == MyProfileViewModel.profile?.id.toString()
) )
} }

View File

@@ -21,10 +21,8 @@ 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
@@ -57,7 +55,6 @@ 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
@@ -69,9 +66,6 @@ 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
@@ -105,10 +99,6 @@ 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) {
@@ -321,40 +311,12 @@ fun IndexScreen() {
), ),
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()),
contentDescription = it.label(), contentDescription = it.label(),
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

@@ -1,7 +1,10 @@
package com.aiosman.ravenow.ui.index.tabs.ai package com.aiosman.ravenow.ui.index.tabs.ai
import android.annotation.SuppressLint
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -18,6 +21,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
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.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
@@ -28,19 +32,27 @@ import androidx.compose.runtime.rememberCoroutineScope
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.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
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.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
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.NavigationRoute
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
import com.aiosman.ravenow.ui.index.tabs.ai.tabs.mine.MineAgent import com.aiosman.ravenow.ui.index.tabs.ai.tabs.mine.MineAgent
import com.aiosman.ravenow.ui.index.tabs.ai.tabs.hot.HotAgent import com.aiosman.ravenow.ui.index.tabs.ai.tabs.hot.HotAgent
import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import com.aiosman.ravenow.ui.composables.TabItem 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.index.tabs.moment.tabs.expolre.AgentItem
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre.ExploreViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@OptIn( ExperimentalFoundationApi::class) @OptIn( ExperimentalFoundationApi::class)
@@ -53,6 +65,9 @@ fun Agent() {
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues() val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
var pagerState = rememberPagerState { 2 } var pagerState = rememberPagerState { 2 }
var scope = rememberCoroutineScope() var scope = rememberCoroutineScope()
val viewModel: AgentViewModel = viewModel()
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@@ -66,10 +81,10 @@ fun Agent() {
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
.height(36.dp) .wrapContentHeight()
.fillMaxWidth(), .fillMaxWidth(),
horizontalArrangement = Arrangement.Start, horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.Bottom verticalAlignment = Alignment.CenterVertically
) { ) {
// 搜索框 // 搜索框
Row( Row(
@@ -115,7 +130,39 @@ fun Agent() {
tint = AppColors.text tint = AppColors.text
) )
} }
Spacer(modifier = Modifier.height(16.dp)) // 推荐Agent
Column(
modifier = Modifier
.fillMaxWidth()
.height(280.dp)
.padding(vertical = 16.dp)
) {
// 标题
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(bottom = 12.dp)
) {
Image(
painter = painterResource(R.mipmap.rider_pro_agent2),
contentDescription = "agent",
modifier = Modifier.size(28.dp),
)
Spacer(modifier = Modifier.width(4.dp))
androidx.compose.material3.Text(
text = stringResource(R.string.agent_recommend_agent),
fontSize = 16.sp,
fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
color = AppColors.text
)
}
// Agent ViewPager
AgentViewPagerSection(agentItems = viewModel.agentItems.take(9),viewModel)
}
Spacer(modifier = Modifier.height(5.dp))
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -192,4 +239,183 @@ fun Agent() {
} }
} }
} }
} }
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun AgentViewPagerSection(agentItems: List<AgentItem>,viewModel: AgentViewModel) {
val AppColors = LocalAppTheme.current
// 每页显示3个agent
val itemsPerPage = 3
val totalPages = (agentItems.size + itemsPerPage - 1) / itemsPerPage
if (totalPages > 0) {
val pagerState = rememberPagerState(pageCount = { totalPages })
Column {
// Agent内容
Box(
modifier = Modifier
.height(180.dp)
) {
HorizontalPager(
state = pagerState,
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(
viewModel = viewModel,
agentItems = agentItems.drop(page * itemsPerPage).take(itemsPerPage),
page = page,
modifier = Modifier.height(180.dp)
.graphicsLayer {
scaleX = scale
scaleY = scale
},
navController = LocalNavController.current,
)
}
}
// 指示器
Row(
modifier = Modifier
.fillMaxWidth()
.height(30.dp)
.padding(top = 12.dp),
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.Center
) {
repeat(totalPages) { index ->
Box(
modifier = Modifier
.padding(horizontal = 4.dp)
.size(3.dp)
.background(
color = if (pagerState.currentPage == index) AppColors.text else AppColors.secondaryText.copy(alpha = 0.3f),
shape = androidx.compose.foundation.shape.CircleShape
)
)
}
}
}
}
}
@Composable
fun AgentPage(viewModel: AgentViewModel,agentItems: List<AgentItem>, page: Int, modifier: Modifier = Modifier,navController: NavHostController) {
Column(
modifier = modifier
.fillMaxSize()
.padding(horizontal = 0.dp)
) {
// 显示3个agent
agentItems.forEachIndexed { index, agentItem ->
AgentCard2(agentItem = agentItem, viewModel = viewModel, navController = LocalNavController.current)
if (index < agentItems.size - 1) {
Spacer(modifier = Modifier.height(8.dp))
}
}
}
}
@SuppressLint("SuspiciousIndentation")
@Composable
fun AgentCard2(viewModel: AgentViewModel,agentItem: AgentItem,navController: NavHostController) {
val AppColors = LocalAppTheme.current
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 3.dp),
verticalAlignment = Alignment.CenterVertically
) {
// 左侧头像
Box(
modifier = Modifier
.size(48.dp)
.background(Color(0xFFF5F5F5), RoundedCornerShape(24.dp)),
contentAlignment = Alignment.Center
) {
if (agentItem.avatar.isNotEmpty()) {
CustomAsyncImage(
imageUrl = agentItem.avatar,
contentDescription = "Agent头像",
modifier = Modifier
.size(48.dp)
.clip(RoundedCornerShape(24.dp)),
contentScale = androidx.compose.ui.layout.ContentScale.Crop
)
} else {
Image(
painter = painterResource(R.mipmap.rider_pro_agent),
contentDescription = "默认头像",
modifier = Modifier.size(24.dp),
colorFilter = ColorFilter.tint(AppColors.secondaryText)
)
}
}
Spacer(modifier = Modifier.width(12.dp))
// 中间文字内容
Column(
modifier = Modifier
.weight(1f)
.padding(end = 8.dp)
) {
// 标题
androidx.compose.material3.Text(
text = agentItem.title,
fontSize = 14.sp,
fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
color = AppColors.text,
maxLines = 1,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
)
Spacer(modifier = Modifier.height(8.dp))
// 描述
androidx.compose.material3.Text(
text = agentItem.desc,
fontSize = 12.sp,
color = AppColors.secondaryText,
maxLines = 1,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
)
}
// 右侧聊天按钮
Box(
modifier = Modifier
.size(width = 60.dp, height = 32.dp)
.background(
color = Color(0X147c7480),
shape = RoundedCornerShape(8.dp)
)
.clickable {
/* viewModel.createSingleChat(agentItem.trtcId)
viewModel.goToChatAi(agentItem.trtcId, navController = navController)*/
},
contentAlignment = Alignment.Center
) {
androidx.compose.material3.Text(
text = stringResource(R.string.chat),
fontSize = 12.sp,
color = AppColors.text,
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
)
}
}
}

View File

@@ -1,7 +1,78 @@
package com.aiosman.ravenow.ui.index.tabs.ai package com.aiosman.ravenow.ui.index.tabs.ai
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController
import com.aiosman.ravenow.data.api.ApiClient
import com.aiosman.ravenow.data.api.RaveNowAPI
import com.aiosman.ravenow.data.api.SingleChatRequestBody
import com.aiosman.ravenow.ui.index.tabs.ai.tabs.mine.MineAgentViewModel.createGroup2ChatAi
import com.aiosman.ravenow.ui.index.tabs.message.MessageListViewModel.userService
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre.AgentItem
import kotlinx.coroutines.launch
object AgentViewModel: ViewModel() { object AgentViewModel: ViewModel() {
private val apiClient: RaveNowAPI = ApiClient.api
var agentItems by mutableStateOf<List<AgentItem>>(emptyList())
private set
var errorMessage by mutableStateOf<String?>(null)
private set
var isRefreshing by mutableStateOf(false)
private set
var isLoading by mutableStateOf(false)
private set
init {
loadAgentData()
}
private fun loadAgentData() {
viewModelScope.launch {
isLoading = true
errorMessage = null
try {
val response = apiClient.getAgent(page = 1, pageSize = 20, withWorkflow = 1)
if (response.isSuccessful) {
val agents = response.body()?.data?.list ?: emptyList()
agentItems = agents.map { agent ->
AgentItem.fromAgent(agent)
}
} else {
errorMessage = "获取Agent数据失败: ${response.code()}"
}
} catch (e: Exception) {
errorMessage = "网络请求失败: ${e.message}"
} finally {
isLoading = false
}
}
}
fun createSingleChat(
openId: String,
) {
viewModelScope.launch {
val response = ApiClient.api.createSingleChat(SingleChatRequestBody(generateText = openId))
}
}
fun goToChatAi(
openId: String,
navController: NavHostController
) {
viewModelScope.launch {
val profile = userService.getUserProfileByOpenId(openId)
createGroup2ChatAi(profile.trtcUserId,"ai_group",navController,profile.id)
}
}
} }

View File

@@ -91,11 +91,11 @@ object AgentChatListViewModel : ViewModel() {
var hasNext by mutableStateOf(true) var hasNext by mutableStateOf(true)
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 val totalUnreadCount: Int
get() = agentChatList.sumOf { it.unreadCount } 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) {

View File

@@ -85,11 +85,11 @@ object FriendChatListViewModel : ViewModel() {
var hasNext by mutableStateOf(true) var hasNext by mutableStateOf(true)
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 val totalUnreadCount: Int
get() = friendChatList.sumOf { it.unreadCount } 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

@@ -102,13 +102,13 @@ object GroupChatListViewModel : ViewModel() {
var hasNext by mutableStateOf(true) var hasNext by mutableStateOf(true)
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 val totalUnreadCount: Int
get() = groupChatList.sumOf { it.unreadCount } get() = groupChatList.sumOf { it.unreadCount }
private val pageSize = 20 private val pageSize = 20
// 消息监听器 // 消息监听器
private var messageListener: V2TIMAdvancedMsgListener? = null private var messageListener: V2TIMAdvancedMsgListener? = null
private var conversationListener: V2TIMConversationListener? = null private var conversationListener: V2TIMConversationListener? = null
@@ -229,7 +229,7 @@ object GroupChatListViewModel : ViewModel() {
loadGroupChatList(context) loadGroupChatList(context)
} }
} }
// 初始化消息监听器 // 初始化消息监听器
fun initMessageListener(context: Context) { fun initMessageListener(context: Context) {
// 消息监听器 - 监听新消息 // 消息监听器 - 监听新消息
@@ -245,7 +245,7 @@ object GroupChatListViewModel : ViewModel() {
} }
} }
} }
// 会话监听器 - 监听会话变化 // 会话监听器 - 监听会话变化
conversationListener = object : V2TIMConversationListener() { conversationListener = object : V2TIMConversationListener() {
override fun onConversationChanged(conversationList: MutableList<V2TIMConversation>?) { override fun onConversationChanged(conversationList: MutableList<V2TIMConversation>?) {
@@ -259,7 +259,7 @@ object GroupChatListViewModel : ViewModel() {
} }
} }
} }
override fun onNewConversation(conversationList: MutableList<V2TIMConversation>?) { override fun onNewConversation(conversationList: MutableList<V2TIMConversation>?) {
super.onNewConversation(conversationList) super.onNewConversation(conversationList)
// 新增会话,刷新群聊列表 // 新增会话,刷新群聊列表
@@ -272,12 +272,12 @@ object GroupChatListViewModel : ViewModel() {
} }
} }
} }
// 注册监听器 // 注册监听器
V2TIMManager.getMessageManager().addAdvancedMsgListener(messageListener) V2TIMManager.getMessageManager().addAdvancedMsgListener(messageListener)
V2TIMManager.getConversationManager().addConversationListener(conversationListener) V2TIMManager.getConversationManager().addConversationListener(conversationListener)
} }
// 移除消息监听器 // 移除消息监听器
fun removeMessageListener() { fun removeMessageListener() {
messageListener?.let { messageListener?.let {
@@ -289,7 +289,7 @@ object GroupChatListViewModel : ViewModel() {
messageListener = null messageListener = null
conversationListener = null conversationListener = null
} }
// 刷新群聊列表 // 刷新群聊列表
private fun refreshGroupChatList(context: Context) { private fun refreshGroupChatList(context: Context) {
viewModelScope.launch { viewModelScope.launch {

View File

@@ -79,7 +79,7 @@ fun MomentsList() {
// center the tabs // center the tabs
horizontalArrangement = Arrangement.Start, horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) {//探索tab ) {
Column( Column(
modifier = Modifier modifier = Modifier
.noRippleClickable { .noRippleClickable {
@@ -108,8 +108,38 @@ fun MomentsList() {
.width(34.dp) .width(34.dp)
.height(4.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)) Spacer(modifier = Modifier.width(16.dp))
Column( Column(
modifier = Modifier modifier = Modifier

View File

@@ -68,6 +68,8 @@ fun Dynamic() {
moments.size, moments.size,
key = { idx -> idx } key = { idx -> idx }
) { idx -> ) { idx ->
//处理下标越界
if (idx < 0 || idx >= moments.size) return@items
val momentItem = moments[idx] ?: return@items val momentItem = moments[idx] ?: return@items
MomentCard(momentEntity = momentItem, MomentCard(momentEntity = momentItem,
onAddComment = { onAddComment = {

View File

@@ -96,7 +96,7 @@ data class AgentItem(
val desc: String, val desc: String,
val avatar: String, val avatar: String,
val useCount: Int, val useCount: Int,
//val author: String //val trtcId: String
) { ) {
companion object { companion object {
fun fromAgent(agent: Agent): AgentItem { fun fromAgent(agent: Agent): AgentItem {
@@ -106,7 +106,7 @@ data class AgentItem(
desc = agent.desc, desc = agent.desc,
avatar = "${ApiClient.BASE_API_URL+"/outside"}${agent.avatar}"+"?token="+"${AppStore.token}", avatar = "${ApiClient.BASE_API_URL+"/outside"}${agent.avatar}"+"?token="+"${AppStore.token}",
useCount = agent.useCount, useCount = agent.useCount,
//author = agent.author // trtcId = agent.
) )
} }
} }
@@ -147,89 +147,89 @@ fun Explore() {
fun AgentCard2(agentItem: AgentItem) { fun AgentCard2(agentItem: AgentItem) {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
Row( Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
// 左侧头像
Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .size(48.dp)
.padding(vertical = 12.dp), .background(Color(0xFFF5F5F5), RoundedCornerShape(24.dp)),
verticalAlignment = Alignment.CenterVertically contentAlignment = Alignment.Center
) { ) {
// 左侧头像 if (agentItem.avatar.isNotEmpty()) {
Box( CustomAsyncImage(
modifier = Modifier imageUrl = agentItem.avatar,
.size(48.dp) contentDescription = "Agent头像",
.background(Color(0xFFF5F5F5), RoundedCornerShape(24.dp)), modifier = Modifier
contentAlignment = Alignment.Center .size(48.dp)
) { .clip(RoundedCornerShape(24.dp)),
if (agentItem.avatar.isNotEmpty()) { contentScale = androidx.compose.ui.layout.ContentScale.Crop
CustomAsyncImage(
imageUrl = agentItem.avatar,
contentDescription = "Agent头像",
modifier = Modifier
.size(48.dp)
.clip(RoundedCornerShape(24.dp)),
contentScale = androidx.compose.ui.layout.ContentScale.Crop
)
} else {
Image(
painter = painterResource(R.mipmap.rider_pro_agent),
contentDescription = "默认头像",
modifier = Modifier.size(24.dp),
colorFilter = ColorFilter.tint(AppColors.secondaryText)
)
}
}
Spacer(modifier = Modifier.width(12.dp))
// 中间文字内容
Column(
modifier = Modifier
.weight(1f)
.padding(end = 8.dp)
) {
// 标题
Text(
text = agentItem.title,
fontSize = 14.sp,
fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
color = AppColors.text,
maxLines = 1,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
) )
} else {
Spacer(modifier = Modifier.height(8.dp)) Image(
painter = painterResource(R.mipmap.rider_pro_agent),
// 描述 contentDescription = "默认头像",
Text( modifier = Modifier.size(24.dp),
text = agentItem.desc, colorFilter = ColorFilter.tint(AppColors.secondaryText)
fontSize = 12.sp,
color = AppColors.secondaryText,
maxLines = 1,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
)
}
// 右侧聊天按钮
Box(
modifier = Modifier
.size(width = 60.dp, height = 32.dp)
.background(
color = Color(0X147c7480),
shape = RoundedCornerShape(8.dp)
)
.clickable {
// 聊天逻辑
},
contentAlignment = Alignment.Center
) {
Text(
text = "聊天",
fontSize = 12.sp,
color = AppColors.text,
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
) )
} }
} }
Spacer(modifier = Modifier.width(12.dp))
// 中间文字内容
Column(
modifier = Modifier
.weight(1f)
.padding(end = 8.dp)
) {
// 标题
Text(
text = agentItem.title,
fontSize = 14.sp,
fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
color = AppColors.text,
maxLines = 1,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
)
Spacer(modifier = Modifier.height(8.dp))
// 描述
Text(
text = agentItem.desc,
fontSize = 12.sp,
color = AppColors.secondaryText,
maxLines = 1,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
)
}
// 右侧聊天按钮
Box(
modifier = Modifier
.size(width = 60.dp, height = 32.dp)
.background(
color = Color(0X147c7480),
shape = RoundedCornerShape(8.dp)
)
.clickable {
// 聊天逻辑
},
contentAlignment = Alignment.Center
) {
Text(
text = "聊天",
fontSize = 12.sp,
color = AppColors.text,
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
)
}
}
} }
@Composable @Composable
@@ -275,9 +275,9 @@ fun Explore() {
) { page -> ) { page ->
// 计算当前页面的偏移量 // 计算当前页面的偏移量
val pageOffset = ( val pageOffset = (
(pagerState.currentPage - page) + pagerState (pagerState.currentPage - page) + pagerState
.currentPageOffsetFraction .currentPageOffsetFraction
).coerceIn(-1f, 1f) ).coerceIn(-1f, 1f)
// 根据偏移量计算缩放比例 // 根据偏移量计算缩放比例
val scale = 1f - (0.1f * kotlin.math.abs(pageOffset)) val scale = 1f - (0.1f * kotlin.math.abs(pageOffset))
@@ -473,22 +473,22 @@ fun Explore() {
verticalArrangement = androidx.compose.foundation.layout.Arrangement.SpaceBetween verticalArrangement = androidx.compose.foundation.layout.Arrangement.SpaceBetween
) { ) {
// 顶部:用户数量 // 顶部:用户数量
Row( Row(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
/* Image( /* Image(
painter = painterResource(R.drawable.rider_pro_nav_profile), painter = painterResource(R.drawable.rider_pro_nav_profile),
contentDescription = "chat", contentDescription = "chat",
modifier = Modifier.size(16.dp), modifier = Modifier.size(16.dp),
colorFilter = ColorFilter.tint(Color.White) colorFilter = ColorFilter.tint(Color.White)
) )
Spacer(modifier = Modifier.width(4.dp)) Spacer(modifier = Modifier.width(4.dp))
Text( Text(
text = "${bannerItem.userCount}人在聊", text = "${bannerItem.userCount}人在聊",
fontSize = 12.sp, fontSize = 12.sp,
color = Color.White, color = Color.White,
fontWeight = androidx.compose.ui.text.font.FontWeight.W500 fontWeight = androidx.compose.ui.text.font.FontWeight.W500
)*/ )*/
} }
// 底部:标题和描述 // 底部:标题和描述
@@ -500,7 +500,7 @@ fun Explore() {
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis, overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
fontWeight = androidx.compose.ui.text.font.FontWeight.Bold, fontWeight = androidx.compose.ui.text.font.FontWeight.Bold,
color = Color.White, color = Color.White,
modifier = Modifier.padding(start = 16.dp) modifier = Modifier.padding(start = 16.dp)
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Text( Text(
@@ -516,81 +516,81 @@ fun Explore() {
Row( Row(
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
.background(brush = androidx.compose.ui.graphics.Brush.verticalGradient( .background(brush = androidx.compose.ui.graphics.Brush.verticalGradient(
colors = listOf( colors = listOf(
Color(0x00000000), // 底部颜色(透明) Color(0x00000000), // 底部颜色(透明)
Color(0x33000000), // 顶部颜色 Color(0x33000000), // 顶部颜色
) )
)), )),
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.SpaceBetween, horizontalArrangement = androidx.compose.foundation.layout.Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Row( modifier = Modifier Row( modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(16.dp), .padding(16.dp),
verticalAlignment = Alignment.CenterVertically){ verticalAlignment = Alignment.CenterVertically){
// 左侧头像 // 左侧头像
Box( Box(
modifier = Modifier modifier = Modifier
.size(24.dp) .size(24.dp)
.background( .background(
Color.White.copy(alpha = 0.2f), Color.White.copy(alpha = 0.2f),
RoundedCornerShape(16.dp) RoundedCornerShape(16.dp)
)
) {
CustomAsyncImage(
context = context,
imageUrl = bannerItem.imageUrl,
contentDescription = "agent Image",
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
.clip(RoundedCornerShape(24.dp)))
}
// 中间信息区域占比1
Column(
modifier = Modifier
.weight(1f)
.padding(horizontal = 8.dp)
) {
Text(
text = bannerItem.agentName,
fontSize = 10.sp,
color = Color(0xfff5f5f5).copy(alpha = 0.6f),
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
) )
Text(
) { text = bannerItem.subtitle,
CustomAsyncImage( fontSize = 12.sp,
context = context, color = Color.White,
imageUrl = bannerItem.imageUrl, maxLines = 1,
contentDescription = "agent Image", fontWeight = androidx.compose.ui.text.font.FontWeight.W400,
contentScale = ContentScale.Crop, modifier = Modifier.padding(top = 2.dp)
modifier = Modifier.fillMaxSize()
.clip(RoundedCornerShape(24.dp)))
}
// 中间信息区域占比1
Column(
modifier = Modifier
.weight(1f)
.padding(horizontal = 8.dp)
) {
Text(
text = bannerItem.agentName,
fontSize = 10.sp,
color = Color(0xfff5f5f5).copy(alpha = 0.6f),
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
)
Text(
text = bannerItem.subtitle,
fontSize = 12.sp,
color = Color.White,
maxLines = 1,
fontWeight = androidx.compose.ui.text.font.FontWeight.W400,
modifier = Modifier.padding(top = 2.dp)
)
}
// 右侧进入按钮
Box(
modifier = Modifier
.width(69.dp)
.height(29.dp)
.background(
color = Color(0x7dffffff),
shape = RoundedCornerShape(8.dp)
) )
.clickable { }
// 进入房间逻辑
}, // 右侧进入按钮
contentAlignment = Alignment.Center Box(
) { modifier = Modifier
Text( .width(69.dp)
text = "进入", .height(29.dp)
fontSize = 14.sp, .background(
color = Color.White, color = Color(0x7dffffff),
fontWeight = androidx.compose.ui.text.font.FontWeight.W600 shape = RoundedCornerShape(8.dp)
) )
} .clickable {
// 进入房间逻辑
},
contentAlignment = Alignment.Center
) {
Text(
text = "进入",
fontSize = 14.sp,
color = Color.White,
fontWeight = androidx.compose.ui.text.font.FontWeight.W600
)
}
} }
@@ -706,7 +706,7 @@ fun Explore() {
contentDescription = "发布动态", contentDescription = "发布动态",
modifier = Modifier.size(24.dp), modifier = Modifier.size(24.dp),
) )
} }
Spacer(modifier = Modifier.size(8.dp)) Spacer(modifier = Modifier.size(8.dp))
Text( Text(
@@ -733,7 +733,7 @@ fun Explore() {
contentDescription = "热门智能体", contentDescription = "热门智能体",
modifier = Modifier.size(24.dp), modifier = Modifier.size(24.dp),
) )
} }
Spacer(modifier = Modifier.size(8.dp)) Spacer(modifier = Modifier.size(8.dp))
Text( Text(
@@ -746,96 +746,96 @@ fun Explore() {
} }
} }
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun BannerSection(bannerItems: List<BannerItem>) { fun BannerSection(bannerItems: List<BannerItem>) {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val pagerState = rememberPagerState(pageCount = { bannerItems.size }) val pagerState = rememberPagerState(pageCount = { bannerItems.size })
// 预加载banner图片 // 预加载banner图片
LaunchedEffect(bannerItems) { LaunchedEffect(bannerItems) {
bannerItems.forEach { bannerItem -> bannerItems.forEach { bannerItem ->
if (bannerItem.backgroundImageUrl.isNotEmpty()) { if (bannerItem.backgroundImageUrl.isNotEmpty()) {
// 预加载背景图片 // 预加载背景图片
com.aiosman.ravenow.utils.Utils.getImageLoader(context).enqueue( com.aiosman.ravenow.utils.Utils.getImageLoader(context).enqueue(
coil.request.ImageRequest.Builder(context) coil.request.ImageRequest.Builder(context)
.data(bannerItem.backgroundImageUrl) .data(bannerItem.backgroundImageUrl)
.memoryCachePolicy(coil.request.CachePolicy.ENABLED) .memoryCachePolicy(coil.request.CachePolicy.ENABLED)
.diskCachePolicy(coil.request.CachePolicy.ENABLED) .diskCachePolicy(coil.request.CachePolicy.ENABLED)
.build() .build()
) )
}
if (bannerItem.imageUrl.isNotEmpty()) {
// 预加载头像图片
com.aiosman.ravenow.utils.Utils.getImageLoader(context).enqueue(
coil.request.ImageRequest.Builder(context)
.data(bannerItem.imageUrl)
.memoryCachePolicy(coil.request.CachePolicy.ENABLED)
.diskCachePolicy(coil.request.CachePolicy.ENABLED)
.build()
)
}
}
}
Column {
// Banner内容
Box(
modifier = Modifier
.fillMaxWidth()
.height(362.dp)
) {
HorizontalPager(
state = pagerState,
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
} }
) if (bannerItem.imageUrl.isNotEmpty()) {
} // 预加载头像图片
} com.aiosman.ravenow.utils.Utils.getImageLoader(context).enqueue(
coil.request.ImageRequest.Builder(context)
.data(bannerItem.imageUrl)
.memoryCachePolicy(coil.request.CachePolicy.ENABLED)
.diskCachePolicy(coil.request.CachePolicy.ENABLED)
.build()
)
}
}
}
// 指示器 Column {
Row( // Banner内容
modifier = Modifier Box(
.fillMaxWidth() modifier = Modifier
.padding(top = 12.dp), .fillMaxWidth()
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.Center .height(362.dp)
) { ) {
bannerItems.forEachIndexed { index, _ -> HorizontalPager(
Box( state = pagerState,
modifier = Modifier modifier = Modifier.fillMaxSize(),
.padding(horizontal = 4.dp) contentPadding = androidx.compose.foundation.layout.PaddingValues(horizontal = 4.dp),
.size(3.dp) ) { page ->
.background( val bannerItem = bannerItems[page]
color = if (pagerState.currentPage == index) AppColors.main else AppColors.secondaryText.copy(alpha = 0.3f),
shape = androidx.compose.foundation.shape.CircleShape // 计算当前页面的偏移量
) 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
}
)
}
}
// 指示器
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 12.dp),
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.Center
) {
bannerItems.forEachIndexed { index, _ ->
Box(
modifier = Modifier
.padding(horizontal = 4.dp)
.size(3.dp)
.background(
color = if (pagerState.currentPage == index) AppColors.main else AppColors.secondaryText.copy(alpha = 0.3f),
shape = androidx.compose.foundation.shape.CircleShape
)
)
}
}
}
} }
}
}
}
// 第二块区域Banner // 第二块区域Banner
@@ -886,7 +886,7 @@ fun BannerSection(bannerItems: List<BannerItem>) {
contentDescription = "agent", contentDescription = "agent",
modifier = Modifier.size(28.dp), modifier = Modifier.size(28.dp),
) )
Spacer(modifier = Modifier.width(4.dp)) Spacer(modifier = Modifier.width(4.dp))
Text( Text(
text = "推荐给你的智能体", text = "推荐给你的智能体",
@@ -939,4 +939,4 @@ fun BannerSection(bannerItems: List<BannerItem>) {
modifier = Modifier.align(Alignment.TopCenter) modifier = Modifier.align(Alignment.TopCenter)
) )
} }
} }

View File

@@ -174,5 +174,13 @@
<string name="group_info_notice_setting">通知</string> <string name="group_info_notice_setting">通知</string>
<string name="group_info_exit">退出</string> <string name="group_info_exit">退出</string>
<string name="group_info_edit">编辑资料</string> <string name="group_info_edit">编辑资料</string>
<string name="group_info">群聊信息</string>
<string name="hot_rooms">热门聊天室</string>
<string name="chat">聊天</string>
<string name="agent_recommend_agent">推荐给你的智能体</string>
<string name="hot_chat_room">正在高能对话中</string>
<string name="create_agent">创建智能体</string>
<string name="publish_dynamic">发布动态</string>
<string name="hot_agent">热门智能体</string>
</resources> </resources>

View File

@@ -169,5 +169,13 @@
<string name="group_info_notice_setting">通知</string> <string name="group_info_notice_setting">通知</string>
<string name="group_info_exit">退出</string> <string name="group_info_exit">退出</string>
<string name="group_info_edit">编辑资料</string> <string name="group_info_edit">编辑资料</string>
<string name="group_info">群聊信息</string>
<string name="hot_rooms">热门聊天室</string>
<string name="chat">聊天</string>
<string name="agent_recommend_agent">推荐给你的智能体</string>
<string name="hot_chat_room">正在高能对话中</string>
<string name="create_agent">创建智能体</string>
<string name="publish_dynamic">发布动态</string>
<string name="hot_agent">热门智能体</string>
</resources> </resources>