This commit is contained in:
weber
2025-08-25 18:35:06 +08:00
parent 77033854f0
commit df75c710e5
20 changed files with 353 additions and 181 deletions

View File

@@ -41,7 +41,7 @@ data class AgentMomentRequestBody(
data class SingleChatRequestBody(
@SerializedName("agentOpenId")
val generateText: String? = null,
val agentOpenId: String? = null,
@SerializedName("agentTrtcId")
val agentTrtcId: String? = null,
)

View File

@@ -4,8 +4,10 @@ import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.aiosman.ravenow.data.ListContainer
import com.aiosman.ravenow.data.AgentService
import com.aiosman.ravenow.data.MomentService
import com.aiosman.ravenow.data.ServiceException
import com.aiosman.ravenow.data.UploadImage
import com.aiosman.ravenow.data.UserService
import com.aiosman.ravenow.data.api.ApiClient
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
@@ -47,6 +49,79 @@ suspend fun createAgent(
}
/**
* 智能体信息分页加载器
*/
class AgentPagingSource(
private val agentRemoteDataSource: AgentRemoteDataSource,
) : PagingSource<Int, AgentEntity>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AgentEntity> {
return try {
val currentPage = params.key ?: 1
val users = agentRemoteDataSource.getAgent(
pageNumber = currentPage
)
LoadResult.Page(
data = users.list,
prevKey = if (currentPage == 1) null else currentPage - 1,
nextKey = if (users.list.isEmpty()) null else users.page + 1
)
} catch (exception: IOException) {
return LoadResult.Error(exception)
}
}
override fun getRefreshKey(state: PagingState<Int, AgentEntity>): Int? {
return state.anchorPosition
}
}
class AgentRemoteDataSource(
private val agentService: AgentService,
) {
suspend fun getAgent(
pageNumber: Int,
): ListContainer<AgentEntity> {
return agentService.getAgent(
pageNumber = pageNumber
)
}
}
class AgentServiceImpl() : AgentService {
val agentBackend = AgentBackend()
override suspend fun getAgent(pageNumber: Int, pageSize: Int): ListContainer<AgentEntity> {
return agentBackend.getAgent(
pageNumber = pageNumber,
)
}
}
class AgentBackend {
val DataBatchSize = 20
suspend fun getAgent(
pageNumber: Int,
): ListContainer<AgentEntity> {
val resp = ApiClient.api.getMyAgent(
pageSize = DataBatchSize,
page = pageNumber,
)
val body = resp.body() ?: throw ServiceException("Failed to get moments")
return ListContainer(
total = body.total,
page = pageNumber,
pageSize = DataBatchSize,
list = body.list.map { it.toAgentEntity() }
)
}
}
data class AgentEntity(
//val author: String,
val avatar: String,

View File

@@ -534,7 +534,7 @@ fun NavHostController.navigateToGroupInfo(id: String) {
val encodedId = java.net.URLEncoder.encode(id, "UTF-8")
navigate(
route = NavigationRoute.ChatGroup.route
route = NavigationRoute.GroupInfo.route
.replace("{id}", encodedId)
)

View File

@@ -58,9 +58,7 @@ object AddAgentViewModel : ViewModel() {
)
println("AddAgentViewModel: Agent created successfully with ID: ${result.id}")
// 通知相关ViewModel更新列表
notifyAgentCreated(result)
return result
} catch (e: Exception) {
@@ -71,10 +69,7 @@ object AddAgentViewModel : ViewModel() {
}
}
private fun notifyAgentCreated(agent: AgentEntity) {
// 通知我的智能体列表更新
com.aiosman.ravenow.ui.index.tabs.ai.tabs.mine.MineAgentViewModel.addAgentToList(agent)
}
fun validate(): String? {
return when {

View File

@@ -199,9 +199,11 @@ fun ChatAiScreen(userId: String) {
color = AppColors.text,
fontSize = 18.sp,
fontWeight = androidx.compose.ui.text.font.FontWeight.W700
)
),
maxLines = 1,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
)
Spacer(modifier = Modifier.weight(1f))
Spacer(modifier = Modifier.width(8.dp))
Box {
Image(
painter = painterResource(R.drawable.rider_pro_more_horizon),

View File

@@ -199,6 +199,7 @@ fun ChatScreen(userId: String) {
modifier = Modifier.weight(1f)
,
maxLines = 1,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
style = TextStyle(
color = AppColors.text,
fontSize = 18.sp,

View File

@@ -206,7 +206,7 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) {
fontWeight = androidx.compose.ui.text.font.FontWeight.W700
),
maxLines = 1,
overflow = TextOverflow.Ellipsis
overflow =TextOverflow.Ellipsis,
)
}
@@ -220,11 +220,13 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) {
color = AppColors.text,
fontSize = 18.sp,
fontWeight = androidx.compose.ui.text.font.FontWeight.Bold
)
),
maxLines = 1,
overflow =TextOverflow.Ellipsis,
)
}
Spacer(modifier = Modifier.weight(1f))
Spacer(modifier = Modifier.width(8.dp))
Box {
Image(
painter = painterResource(R.drawable.rider_pro_more_horizon),

View File

@@ -59,16 +59,15 @@ fun AgentCard(
Row(
modifier = Modifier
) {
// 使用remember基于agentEntity.id来缓存图片避免滑动时重复加载
Box(
modifier = Modifier
.size(40.dp)
.clip(RoundedCornerShape(40.dp))
) {
CustomAsyncImage(
LocalContext.current,
context,
agentEntity.avatar,
contentDescription = "",
contentDescription = agentEntity.openId,
modifier = Modifier.size(40.dp),
contentScale = ContentScale.Crop
)

View File

@@ -23,6 +23,28 @@ import com.aiosman.ravenow.utils.Utils.getImageLoader
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
/**
* CustomAsyncImage 组件使用说明:
*
* @param context 上下文,可选
* @param imageUrl 图片URL或Bitmap对象
* @param contentDescription 图片描述
* @param modifier 修饰符
* @param blurHash 模糊哈希值(暂未使用)
* @param placeholderRes 加载时显示的占位符图片资源ID
* @param errorRes 加载失败时显示的错误图片资源ID
* @param defaultRes 当imageUrl为空或null时显示的默认图片资源ID优先级最高
* @param contentScale 图片缩放模式
*
* 使用示例:
* CustomAsyncImage(
* imageUrl = "https://example.com/image.jpg",
* contentDescription = "用户头像",
* defaultRes = R.mipmap.default_avatar,
* placeholderRes = R.mipmap.loading_placeholder,
* errorRes = R.mipmap.error_image
* )
*/
@Composable
fun rememberImageBitmap(imageUrl: String, imageLoader: ImageLoader): Bitmap? {
val context = LocalContext.current
@@ -53,6 +75,10 @@ fun CustomAsyncImage(
blurHash: String? = null,
@DrawableRes
placeholderRes: Int? = null,
@DrawableRes
errorRes: Int? = null,
@DrawableRes
defaultRes: Int? = null,
contentScale: ContentScale = ContentScale.Crop
) {
val localContext = LocalContext.current
@@ -62,10 +88,11 @@ fun CustomAsyncImage(
// 处理 imageUrl 为 null 或空字符串的情况
if (imageUrl == null || imageUrl == "") {
// 如果 imageUrl 为 null 且有占位符,则直接显示占位符
if (placeholderRes != null) {
// 优先使用 defaultRes然后是 placeholderRes
val fallbackRes = defaultRes ?: placeholderRes
if (fallbackRes != null) {
Image(
painter = androidx.compose.ui.res.painterResource(placeholderRes),
painter = androidx.compose.ui.res.painterResource(fallbackRes),
contentDescription = contentDescription,
modifier = modifier,
contentScale = contentScale
@@ -97,6 +124,10 @@ fun CustomAsyncImage(
if (placeholderRes != null) {
placeholder(placeholderRes)
}
// 设置错误时显示的图片
if (errorRes != null) {
error(errorRes)
}
}
.build(),
contentDescription = contentDescription,
@@ -104,4 +135,38 @@ fun CustomAsyncImage(
contentScale = contentScale,
imageLoader = imageLoader
)
}
}
/*
使用示例:
1. 基本使用(带默认图片):
CustomAsyncImage(
imageUrl = user.avatar,
contentDescription = "用户头像",
defaultRes = R.mipmap.default_avatar
)
2. 完整配置:
CustomAsyncImage(
imageUrl = "https://example.com/image.jpg",
contentDescription = "产品图片",
defaultRes = R.mipmap.default_product,
placeholderRes = R.mipmap.loading_placeholder,
errorRes = R.mipmap.error_image,
contentScale = ContentScale.Crop
)
3. 处理空URL
CustomAsyncImage(
imageUrl = "", // 空字符串会显示默认图片
contentDescription = "头像",
defaultRes = R.mipmap.default_avatar
)
4. 处理Bitmap
CustomAsyncImage(
imageUrl = bitmapObject, // Bitmap对象会直接显示
contentDescription = "裁剪后的图片"
)
*/

View File

@@ -187,11 +187,11 @@ fun GroupChatInfoScreen(groupId: String) {
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.noRippleClickable {
viewModel.viewModelScope.launch {
if (viewModel.notificationStrategy == "mute") {
/*if (viewModel.notificationStrategy == "mute") {
viewModel.updateNotificationStrategy("active")
} else {
viewModel.updateNotificationStrategy("mute")
}
}*/
}
}
) {

View File

@@ -61,7 +61,7 @@ object AgentViewModel: ViewModel() {
openId: String,
) {
viewModelScope.launch {
val response = ApiClient.api.createSingleChat(SingleChatRequestBody(generateText = openId))
val response = ApiClient.api.createSingleChat(SingleChatRequestBody(agentOpenId = openId))
}
}

View File

@@ -114,7 +114,7 @@ object HotAgentViewModel : ViewModel() {
openId: String,
) {
viewModelScope.launch {
val response = ApiClient.api.createSingleChat(SingleChatRequestBody(generateText = openId))
val response = ApiClient.api.createSingleChat(SingleChatRequestBody(agentOpenId = openId))
}
}

View File

@@ -28,6 +28,7 @@ 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.paging.compose.collectAsLazyPagingItems
import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.LocalNavController
import com.aiosman.ravenow.R
@@ -39,13 +40,17 @@ fun MineAgent() {
val AppColors = LocalAppTheme.current
val navController = LocalNavController.current
val model = MineAgentViewModel
var agentList = model.agentList
val scope = rememberCoroutineScope()
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
model.refreshPager(pullRefresh = true)
model.refreshPager(
//pullRefresh = true
)
})
val listState = rememberLazyListState()
var dataFlow = model.agentList
var agentList = dataFlow.collectAsLazyPagingItems()
// observe list scrolling
val reachedBottom by remember {
derivedStateOf {
@@ -56,33 +61,19 @@ fun MineAgent() {
// load more if scrolled to bottom
LaunchedEffect(reachedBottom) {
if (reachedBottom && !model.isLoading && model.hasNext) {
model.loadMore()
}
}
// 只在首次加载时刷新避免从AddAgent返回时重复刷新
LaunchedEffect(Unit) {
if (model.agentList.isEmpty() && !model.isLoading) {
if (reachedBottom) {
model.refreshPager()
}
}
val context = LocalContext.current
// 当智能体列表加载完成后,预加载图片
LaunchedEffect(agentList) {
if (agentList.isNotEmpty()) {
model.preloadImages(context)
}
LaunchedEffect(Unit) {
model.refreshPager()
}
Column(
modifier = Modifier
.fillMaxSize()
) {
if(agentList.isEmpty() && !model.isLoading) {
if(agentList.itemCount == 0 && !model.isLoading) {
Box(
modifier = Modifier
.fillMaxSize(),
@@ -119,23 +110,21 @@ fun MineAgent() {
modifier = Modifier.fillMaxSize(),
state = listState
) {
items(
agentList.size,
key = { idx -> agentList[idx].id } // 使用智能体ID作为key避免重新创建
) { idx ->
val agentItem = agentList[idx]
AgentCard(
agentEntity = agentItem,
onClick = {
model.createSingleChat(agentItem.openId)
model.goToChatAi(agentItem.openId,navController)
},
items(agentList.itemCount) { index ->
agentList[index]?.let { agent ->
AgentCard(
agentEntity = agent,
onClick = {
model.createSingleChat(agent.openId)
model.goToChatAi(agent.openId, navController)
},
)
)
}
}
// 加载更多指示器
if (model.isLoading && agentList.isNotEmpty()) {
if (model.isLoading && agentList.itemCount != 0) {
item {
Box(
modifier = Modifier

View File

@@ -6,19 +6,42 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.aiosman.ravenow.data.Agent
import com.aiosman.ravenow.data.AgentService
import com.aiosman.ravenow.data.MomentService
import com.aiosman.ravenow.data.api.ApiClient
import com.aiosman.ravenow.data.ServiceException
import com.aiosman.ravenow.data.api.SingleChatRequestBody
import com.aiosman.ravenow.entity.AgentEntity
import com.aiosman.ravenow.entity.AgentPagingSource
import com.aiosman.ravenow.entity.AgentRemoteDataSource
import com.aiosman.ravenow.entity.AgentServiceImpl
import com.aiosman.ravenow.entity.MomentEntity
import com.aiosman.ravenow.entity.MomentPagingSource
import com.aiosman.ravenow.entity.MomentRemoteDataSource
import com.aiosman.ravenow.entity.MomentServiceImpl
import com.aiosman.ravenow.ui.index.tabs.message.MessageListViewModel.userService
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.hot.HotMomentViewModel.firstLoad
import com.aiosman.ravenow.ui.navigateToChatAi
import com.tencent.imsdk.v2.V2TIMConversationOperationResult
import com.tencent.imsdk.v2.V2TIMManager
import com.tencent.imsdk.v2.V2TIMValueCallback
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
object MineAgentViewModel : ViewModel() {
var agentList by mutableStateOf<List<AgentEntity>>(emptyList())
private val agentService: AgentService = AgentServiceImpl()
private val _agentList =
MutableStateFlow<PagingData<AgentEntity>>(PagingData.empty())
val agentList = _agentList.asStateFlow()
var refreshing by mutableStateOf(false)
var isLoading by mutableStateOf(false)
var hasNext by mutableStateOf(true)
@@ -29,87 +52,25 @@ object MineAgentViewModel : ViewModel() {
private val preloadedImageIds = mutableSetOf<Int>()
private val pageSize = 20
init {
// 延迟初始化,避免在页面切换时立即加载
// refreshPager()
}
fun refreshPager(pullRefresh: Boolean = false) {
if (isLoading && !pullRefresh) return
viewModelScope.launch {
try {
isLoading = true
refreshing = pullRefresh
error = null
// 清除预加载记录,强制重新加载图片
if (pullRefresh) {
clearPreloadedImages()
}
val response = ApiClient.api.getMyAgent(
page = 1,
pageSize = pageSize
)
val body = response.body()
if (body != null) {
val newAgents = body.list.map { it.toAgentEntity() }
// 只有在列表为空或者是下拉刷新时才替换整个列表
if (agentList.isEmpty() || pullRefresh) {
agentList = newAgents
} else {
// 否则只添加新的智能体
val existingIds = agentList.map { it.id }.toSet()
val newAgentsToAdd = newAgents.filter { it.id !in existingIds }
if (newAgentsToAdd.isNotEmpty()) {
agentList = agentList + newAgentsToAdd
}
}
currentPage = 1
hasNext = newAgents.size == pageSize
} else {
throw ServiceException("Failed to load agents")
}
} catch (e: Exception) {
error = e.message ?: "加载失败"
e.printStackTrace()
} finally {
isLoading = false
refreshing = false
}
fun refreshPager() {
if (!firstLoad) {
return
}
}
fun loadMore() {
if (isLoading || !hasNext) return
firstLoad = false
viewModelScope.launch {
try {
isLoading = true
error = null
val response = ApiClient.api.getMyAgent(
page = currentPage + 1,
pageSize = pageSize
)
val body = response.body()
if (body != null) {
val newAgents = body.list.map { it.toAgentEntity() }
agentList = agentList + newAgents
currentPage += 1
hasNext = newAgents.size == pageSize
} else {
throw ServiceException("Failed to load more agents")
Pager(
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
pagingSourceFactory = {
AgentPagingSource(
AgentRemoteDataSource(agentService),
//trend = true
)
}
} catch (e: Exception) {
error = e.message ?: "加载更多失败"
e.printStackTrace()
} finally {
isLoading = false
).flow.cachedIn(viewModelScope).collectLatest {
_agentList.value = it
}
}
}
@@ -144,7 +105,7 @@ object MineAgentViewModel : ViewModel() {
openId: String,
) {
viewModelScope.launch {
val response = ApiClient.api.createSingleChat(SingleChatRequestBody(generateText = openId))
val response = ApiClient.api.createSingleChat(SingleChatRequestBody(agentOpenId = openId))
}
}
@@ -158,36 +119,5 @@ object MineAgentViewModel : ViewModel() {
}
}
// 添加新创建的智能体到列表顶部
fun addAgentToList(agent: AgentEntity) {
agentList = listOf(agent) + agentList
}
// 预加载图片,避免滑动时重复加载
fun preloadImages(context: android.content.Context) {
viewModelScope.launch {
agentList.forEach { agent ->
if (agent.id !in preloadedImageIds && agent.avatar.isNotEmpty()) {
try {
// 预加载头像图片到缓存
com.aiosman.ravenow.utils.Utils.getImageLoader(context).enqueue(
coil.request.ImageRequest.Builder(context)
.data(agent.avatar)
.memoryCachePolicy(coil.request.CachePolicy.ENABLED)
.diskCachePolicy(coil.request.CachePolicy.ENABLED)
.build()
)
preloadedImageIds.add(agent.id)
} catch (e: Exception) {
// 忽略预加载错误
}
}
}
}
}
// 清除预加载记录(在刷新时调用)
fun clearPreloadedImages() {
preloadedImageIds.clear()
}
}

View File

@@ -175,7 +175,7 @@ fun FriendChatItem(
CustomAsyncImage(
context = LocalContext.current,
imageUrl = conversation.avatar,
contentDescription = conversation.nickname,
contentDescription = conversation.trtcUserId,
modifier = Modifier
.size(48.dp)
.clip(RoundedCornerShape(48.dp))

View File

@@ -1,6 +1,7 @@
package com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre
import android.annotation.SuppressLint
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
@@ -49,6 +50,7 @@ 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 androidx.compose.ui.res.stringResource
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
import androidx.lifecycle.viewmodel.compose.viewModel
import com.aiosman.ravenow.AppStore
@@ -73,6 +75,8 @@ data class BannerItem(
val backgroundImageUrl: String,
val userCount: Int,
val agentName: String,
val trtcId: String,
val avatar: String
) {
companion object {
fun fromRoom(room: Room): BannerItem {
@@ -83,7 +87,18 @@ data class BannerItem(
imageUrl = "${ApiClient.RETROFIT_URL}${room.creator.profile.avatar}"+"?token="+"${AppStore.token}" ?: "",
backgroundImageUrl = "${ApiClient.BASE_API_URL+"/outside"}${room.recommendBanner}"+"?token="+"${AppStore.token}" ?: "",
userCount = room.userCount,
agentName = room.creator.profile.nickname
agentName = room.creator.profile.nickname,
trtcId = room.trtcRoomId,
avatar = if (room.avatar.isNullOrEmpty()) {
// 将 groupId 转换为 Base64
val groupIdBase64 = android.util.Base64.encodeToString(
room.trtcType.toByteArray(),
android.util.Base64.NO_WRAP
)
"${ApiClient.RETROFIT_URL+"group/avatar?groupIdBase64="}${groupIdBase64}"+"&token="+"${AppStore.token}"
} else {
"${ApiClient.BASE_API_URL+"/outside/"}${room.avatar}"+"?token="+"${AppStore.token}"
}
)
}
}
@@ -96,7 +111,7 @@ data class AgentItem(
val desc: String,
val avatar: String,
val useCount: Int,
//val trtcId: String
val openId: String
) {
companion object {
fun fromAgent(agent: Agent): AgentItem {
@@ -106,7 +121,7 @@ data class AgentItem(
desc = agent.desc,
avatar = "${ApiClient.BASE_API_URL+"/outside"}${agent.avatar}"+"?token="+"${AppStore.token}",
useCount = agent.useCount,
// trtcId = agent.
openId = agent.openId,
)
}
}
@@ -126,6 +141,9 @@ fun Explore() {
// 模拟刷新状态
var isRefreshing by remember { mutableStateOf(false) }
val enterSuccessText = stringResource(R.string.group_room_enter_success)
val enterFailText = stringResource(R.string.group_room_enter_fail) // 假设有这个资源
// 监听ViewModel的刷新状态
LaunchedEffect(viewModel.isRefreshing) {
isRefreshing = viewModel.isRefreshing
@@ -218,12 +236,13 @@ fun Explore() {
shape = RoundedCornerShape(8.dp)
)
.clickable {
// 聊天逻辑
viewModel.createSingleChat(agentItem.openId)
viewModel.goToChatAi(agentItem.openId, navController = navController)
},
contentAlignment = Alignment.Center
) {
Text(
text = "聊天",
text = stringResource(R.string.chat),
fontSize = 12.sp,
color = AppColors.text,
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
@@ -320,7 +339,7 @@ fun Explore() {
@Composable
fun HotChatRoomGridItem(roomItem: BannerItem) {
val AppColors = LocalAppTheme.current
val context = LocalContext.current
Row(
modifier = Modifier
.padding(horizontal = 2.dp, vertical = 4.dp)
@@ -332,7 +351,22 @@ fun Explore() {
4 -> Color(0x28af52de)
else -> Color(0x28ffcc00)
},
shape = RoundedCornerShape(12.dp)),
shape = RoundedCornerShape(12.dp))
.clickable {
// 调用加入房间接口
viewModel.joinRoom(
trtcId = roomItem.trtcId.toString(),
name = roomItem.title,
avatar = roomItem.avatar,
navController = navController,
onSuccess = {
Toast.makeText(context, enterSuccessText, Toast.LENGTH_SHORT).show()
},
onError = { errorMessage ->
Toast.makeText(context, enterFailText, Toast.LENGTH_SHORT).show()
}
)
},
verticalAlignment = Alignment.CenterVertically
) {
@@ -427,9 +461,10 @@ fun Explore() {
@Composable
fun BannerCard(bannerItem: BannerItem, modifier: Modifier = Modifier) {
fun BannerCard(bannerItem: BannerItem, viewModel: ExploreViewModel, modifier: Modifier = Modifier) {
val AppColors = LocalAppTheme.current
val context = LocalContext.current
val navController = LocalNavController.current
Card(
modifier = modifier
@@ -580,12 +615,24 @@ fun Explore() {
shape = RoundedCornerShape(8.dp)
)
.clickable {
// 入房间逻辑
// 调用加入房间接口
viewModel.joinRoom(
trtcId = bannerItem.trtcId.toString(),
name = bannerItem.title,
avatar = bannerItem.avatar,
navController = navController,
onSuccess = {
Toast.makeText(context, enterSuccessText, Toast.LENGTH_SHORT).show()
},
onError = { errorMessage ->
Toast.makeText(context, enterFailText, Toast.LENGTH_SHORT).show()
}
)
},
contentAlignment = Alignment.Center
) {
Text(
text = "进入",
text = stringResource(R.string.group_room_enter),
fontSize = 14.sp,
color = Color.White,
fontWeight = androidx.compose.ui.text.font.FontWeight.W600
@@ -806,6 +853,7 @@ fun Explore() {
BannerCard(
bannerItem = bannerItem,
viewModel = viewModel,
modifier = Modifier
.graphicsLayer {
scaleX = scale

View File

@@ -5,10 +5,18 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController
import com.aiosman.ravenow.data.Room
import com.aiosman.ravenow.data.Agent
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.data.api.JoinGroupChatRequestBody
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.message.tab.GroupChatListViewModel
import com.aiosman.ravenow.ui.index.tabs.message.tab.GroupChatListViewModel.createGroupChat
import com.aiosman.ravenow.ui.navigateToGroupChat
import kotlinx.coroutines.launch
class ExploreViewModel : ViewModel() {
@@ -122,5 +130,57 @@ class ExploreViewModel : ViewModel() {
}
}
}
fun createSingleChat(
openId: String,
) {
viewModelScope.launch {
val response = ApiClient.api.createSingleChat(SingleChatRequestBody(agentOpenId = openId))
}
}
fun goToChatAi(
openId: String,
navController: NavHostController
) {
viewModelScope.launch {
val profile = userService.getUserProfileByOpenId(openId)
createGroup2ChatAi(profile.trtcUserId,"ai_group",navController,profile.id)
}
}
fun joinRoom(
trtcId: String,
name: String,
avatar: String,
navController: NavHostController,
onSuccess: () -> Unit,
onError: (String) -> Unit
) {
viewModelScope.launch {
try {
val response = apiClient.joinRoom(JoinGroupChatRequestBody(trtcId = trtcId))
if (response.isSuccessful) {
viewModelScope.launch {
try {
createGroupChat(trtcGroupId = trtcId)
// 群聊直接使用群ID进行导航
navController.navigateToGroupChat( id = trtcId,
name = name,
avatar = avatar)
} catch (e: Exception) {
onError("加入房间失败")
e.printStackTrace()
}
}
onSuccess()
} else {
onError("加入房间失败")
}
} catch (e: Exception) {
onError("网络请求失败:${e.message}")
}
}
}
}

View File

@@ -58,7 +58,7 @@ fun HotMomentsList() {
val navigationBarPaddings =
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp
LaunchedEffect(Unit) {
HotMomentViewModel.refreshPager()
model.refreshPager()
}
var refreshing by remember { mutableStateOf(false) }
val state = rememberPullRefreshState(refreshing, onRefresh = {