Compare commits
3 Commits
revert-48-
...
zhong
| Author | SHA1 | Date | |
|---|---|---|---|
| 85141fde1b | |||
| 234c07142c | |||
| 89227edccf |
@@ -67,7 +67,7 @@ data class SendChatAiRequestBody(
|
|||||||
@SerializedName("skipTrtc")
|
@SerializedName("skipTrtc")
|
||||||
val skipTrtc: Boolean? = true,
|
val skipTrtc: Boolean? = true,
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
data class CreateGroupChatRequestBody(
|
data class CreateGroupChatRequestBody(
|
||||||
@@ -77,11 +77,13 @@ data class CreateGroupChatRequestBody(
|
|||||||
val userIds: List<String>,
|
val userIds: List<String>,
|
||||||
@SerializedName("promptIds")
|
@SerializedName("promptIds")
|
||||||
val promptIds: List<String>,
|
val promptIds: List<String>,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class JoinGroupChatRequestBody(
|
data class JoinGroupChatRequestBody(
|
||||||
@SerializedName("trtcId")
|
@SerializedName("trtcId")
|
||||||
val trtcId: String,
|
val trtcId: String? = null,
|
||||||
|
@SerializedName("roomId")
|
||||||
|
val roomId: Int? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class LoginUserRequestBody(
|
data class LoginUserRequestBody(
|
||||||
@@ -271,6 +273,132 @@ data class RemoveAccountRequestBody(
|
|||||||
val password: String,
|
val password: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// API 错误响应(用于加入房间等接口的错误处理)
|
||||||
|
data class ApiErrorResponse(
|
||||||
|
@SerializedName("err")
|
||||||
|
val error: String,
|
||||||
|
@SerializedName("success")
|
||||||
|
val success: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
// 群聊中的用户信息
|
||||||
|
data class GroupChatUser(
|
||||||
|
@SerializedName("ID")
|
||||||
|
val id: Int,
|
||||||
|
@SerializedName("CreatedAt")
|
||||||
|
val createdAt: String,
|
||||||
|
@SerializedName("UpdatedAt")
|
||||||
|
val updatedAt: String,
|
||||||
|
@SerializedName("DeletedAt")
|
||||||
|
val deletedAt: String?,
|
||||||
|
@SerializedName("userSessionId")
|
||||||
|
val userSessionId: String,
|
||||||
|
@SerializedName("sessions")
|
||||||
|
val sessions: Any?, // 根据实际需要可以定义具体类型
|
||||||
|
@SerializedName("prompts")
|
||||||
|
val prompts: Any?, // 根据实际需要可以定义具体类型
|
||||||
|
@SerializedName("isAgent")
|
||||||
|
val isAgent: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
// 智能体角色信息
|
||||||
|
data class GroupChatPrompt(
|
||||||
|
@SerializedName("ID")
|
||||||
|
val id: Int,
|
||||||
|
@SerializedName("CreatedAt")
|
||||||
|
val createdAt: String,
|
||||||
|
@SerializedName("UpdatedAt")
|
||||||
|
val updatedAt: String,
|
||||||
|
@SerializedName("DeletedAt")
|
||||||
|
val deletedAt: String?,
|
||||||
|
@SerializedName("Title")
|
||||||
|
val title: String,
|
||||||
|
@SerializedName("Desc")
|
||||||
|
val desc: String,
|
||||||
|
@SerializedName("Value")
|
||||||
|
val value: String,
|
||||||
|
@SerializedName("Enable")
|
||||||
|
val enable: Boolean,
|
||||||
|
@SerializedName("UserSessions")
|
||||||
|
val userSessions: Any?, // 根据实际需要可以定义具体类型
|
||||||
|
@SerializedName("Avatar")
|
||||||
|
val avatar: String,
|
||||||
|
@SerializedName("AuthorId")
|
||||||
|
val authorId: Int?,
|
||||||
|
@SerializedName("Author")
|
||||||
|
val author: Any?, // 根据实际需要可以定义具体类型
|
||||||
|
@SerializedName("TokenCount")
|
||||||
|
val tokenCount: Int,
|
||||||
|
@SerializedName("OpenId")
|
||||||
|
val openId: String,
|
||||||
|
@SerializedName("Public")
|
||||||
|
val public: Boolean,
|
||||||
|
@SerializedName("BreakMode")
|
||||||
|
val breakMode: Boolean,
|
||||||
|
@SerializedName("DocNamespace")
|
||||||
|
val docNamespace: String,
|
||||||
|
@SerializedName("UseRag")
|
||||||
|
val useRag: Boolean,
|
||||||
|
@SerializedName("RagThreshold")
|
||||||
|
val ragThreshold: Double,
|
||||||
|
@SerializedName("WorkflowId")
|
||||||
|
val workflowId: Int?,
|
||||||
|
@SerializedName("Workflow")
|
||||||
|
val workflow: Any?, // 根据实际需要可以定义具体类型
|
||||||
|
@SerializedName("WorkflowInputs")
|
||||||
|
val workflowInputs: Any?, // 根据实际需要可以定义具体类型
|
||||||
|
@SerializedName("Source")
|
||||||
|
val source: String,
|
||||||
|
@SerializedName("categories")
|
||||||
|
val categories: Any? // 根据实际需要可以定义具体类型
|
||||||
|
)
|
||||||
|
|
||||||
|
// 群聊详细信息响应
|
||||||
|
data class GroupChatResponse(
|
||||||
|
@SerializedName("ID")
|
||||||
|
val id: Int,
|
||||||
|
@SerializedName("CreatedAt")
|
||||||
|
val createdAt: String,
|
||||||
|
@SerializedName("UpdatedAt")
|
||||||
|
val updatedAt: String,
|
||||||
|
@SerializedName("DeletedAt")
|
||||||
|
val deletedAt: String?,
|
||||||
|
@SerializedName("name")
|
||||||
|
val name: String,
|
||||||
|
@SerializedName("description")
|
||||||
|
val description: String,
|
||||||
|
@SerializedName("creatorId")
|
||||||
|
val creatorId: Int,
|
||||||
|
@SerializedName("creator")
|
||||||
|
val creator: Any?, // 根据实际需要可以定义具体类型
|
||||||
|
@SerializedName("trtcRoomId")
|
||||||
|
val trtcRoomId: String,
|
||||||
|
@SerializedName("trtcType")
|
||||||
|
val trtcType: String,
|
||||||
|
@SerializedName("cover")
|
||||||
|
val cover: String,
|
||||||
|
@SerializedName("avatar")
|
||||||
|
val avatar: String,
|
||||||
|
@SerializedName("recommendBanner")
|
||||||
|
val recommendBanner: String,
|
||||||
|
@SerializedName("isRecommended")
|
||||||
|
val isRecommended: Boolean,
|
||||||
|
@SerializedName("allowInHot")
|
||||||
|
val allowInHot: Boolean,
|
||||||
|
@SerializedName("users")
|
||||||
|
val users: List<GroupChatUser>,
|
||||||
|
@SerializedName("prompts")
|
||||||
|
val prompts: List<GroupChatPrompt>,
|
||||||
|
@SerializedName("source")
|
||||||
|
val source: String
|
||||||
|
)
|
||||||
|
class CategoryTemplateTranslation(
|
||||||
|
@SerializedName("name")
|
||||||
|
val name: String,
|
||||||
|
@SerializedName("description")
|
||||||
|
val description: String,
|
||||||
|
)
|
||||||
|
|
||||||
data class CategoryTemplate(
|
data class CategoryTemplate(
|
||||||
@SerializedName("id")
|
@SerializedName("id")
|
||||||
val id: Int,
|
val id: Int,
|
||||||
@@ -295,19 +423,19 @@ data class CategoryTemplate(
|
|||||||
@SerializedName("createdAt")
|
@SerializedName("createdAt")
|
||||||
val createdAt: String,
|
val createdAt: String,
|
||||||
@SerializedName("updatedAt")
|
@SerializedName("updatedAt")
|
||||||
val updatedAt: String
|
val updatedAt: String,
|
||||||
)
|
@SerializedName("translations")
|
||||||
|
val translations: Map<String, CategoryTemplateTranslation>?
|
||||||
data class CategoryListResponse(
|
) {
|
||||||
@SerializedName("page")
|
/**
|
||||||
val page: Int,
|
* 获取本地化名称,优先使用当前语言的翻译,如果没有则使用默认名称
|
||||||
@SerializedName("pageSize")
|
*/
|
||||||
val pageSize: Int,
|
fun getLocalizedName(): String {
|
||||||
@SerializedName("total")
|
// 这里可以根据需要添加国际化逻辑
|
||||||
val total: Int,
|
// 目前直接返回默认名称
|
||||||
@SerializedName("list")
|
return name
|
||||||
val list: List<CategoryTemplate>
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
interface RaveNowAPI {
|
interface RaveNowAPI {
|
||||||
@GET("membership/config")
|
@GET("membership/config")
|
||||||
@@ -588,9 +716,26 @@ interface RaveNowAPI {
|
|||||||
suspend fun getAgent(
|
suspend fun getAgent(
|
||||||
@Query("page") page: Int = 1,
|
@Query("page") page: Int = 1,
|
||||||
@Query("pageSize") pageSize: Int = 20,
|
@Query("pageSize") pageSize: Int = 20,
|
||||||
@Query("withWorkflow") withWorkflow: Int = 1,
|
@Query("order") order: String? = null,
|
||||||
|
@Query("orderKey") orderKey: String? = null,
|
||||||
|
@Query("createdAt") createdAt: String? = null,
|
||||||
|
@Query("updatedAt") updatedAt: String? = null,
|
||||||
|
@Query("createdStart") createdStart: String? = null,
|
||||||
|
@Query("createdEnd") createdEnd: String? = null,
|
||||||
|
@Query("updatedStart") updatedStart: String? = null,
|
||||||
|
@Query("updatedEnd") updatedEnd: String? = null,
|
||||||
|
@Query("title") title: String? = null,
|
||||||
@Query("authorId") authorId: Int? = null,
|
@Query("authorId") authorId: Int? = null,
|
||||||
|
@Query("authorOpenId") authorOpenId: String? = null,
|
||||||
|
@Query("showPrivate") showPrivate: String? = null,
|
||||||
|
@Query("explore") explore: String? = null,
|
||||||
|
@Query("desc") desc: String? = null,
|
||||||
|
@Query("withWorkflow") withWorkflow: String? = null,
|
||||||
|
@Query("hasAvatar") hasAvatar: String? = null,
|
||||||
|
@Query("random") random: String? = null,
|
||||||
|
@Query("categoryName") categoryName: String? = null,
|
||||||
@Query("categoryIds") categoryIds: List<Int>? = null,
|
@Query("categoryIds") categoryIds: List<Int>? = null,
|
||||||
|
@Query("uncategorized") uncategorized: String? = null,
|
||||||
): Response<DataContainer<ListContainer<Agent>>>
|
): Response<DataContainer<ListContainer<Agent>>>
|
||||||
|
|
||||||
@GET("outside/my/prompts")
|
@GET("outside/my/prompts")
|
||||||
@@ -619,7 +764,10 @@ interface RaveNowAPI {
|
|||||||
suspend fun agentMoment(@Body body: AgentMomentRequestBody): Response<DataContainer<String>>
|
suspend fun agentMoment(@Body body: AgentMomentRequestBody): Response<DataContainer<String>>
|
||||||
|
|
||||||
@GET("outside/rooms/open")
|
@GET("outside/rooms/open")
|
||||||
suspend fun createGroupChatAi(@Query("trtcGroupId") trtcGroupId: String): Response<DataContainer<Unit>>
|
suspend fun createGroupChatAi(
|
||||||
|
@Query("trtcGroupId") trtcGroupId: String? = null,
|
||||||
|
@Query("roomId") roomId: Int? = null
|
||||||
|
): Response<DataContainer<GroupChatResponse>>
|
||||||
|
|
||||||
@POST("outside/rooms/create-single-chat")
|
@POST("outside/rooms/create-single-chat")
|
||||||
suspend fun createSingleChat(@Body body: SingleChatRequestBody): Response<DataContainer<Unit>>
|
suspend fun createSingleChat(@Body body: SingleChatRequestBody): Response<DataContainer<Unit>>
|
||||||
@@ -634,6 +782,7 @@ interface RaveNowAPI {
|
|||||||
suspend fun getRooms(@Query("page") page: Int = 1,
|
suspend fun getRooms(@Query("page") page: Int = 1,
|
||||||
@Query("pageSize") pageSize: Int = 20,
|
@Query("pageSize") pageSize: Int = 20,
|
||||||
@Query("isRecommended") isRecommended: Int = 1,
|
@Query("isRecommended") isRecommended: Int = 1,
|
||||||
|
@Query("random") random: Int? = null,
|
||||||
): Response<ListContainer<Room>>
|
): Response<ListContainer<Room>>
|
||||||
|
|
||||||
@GET("outside/rooms/detail")
|
@GET("outside/rooms/detail")
|
||||||
@@ -655,7 +804,7 @@ interface RaveNowAPI {
|
|||||||
@Query("withParent") withParent: Boolean? = null,
|
@Query("withParent") withParent: Boolean? = null,
|
||||||
@Query("withCount") withCount: Boolean? = null,
|
@Query("withCount") withCount: Boolean? = null,
|
||||||
@Query("hideEmpty") hideEmpty: Boolean? = null
|
@Query("hideEmpty") hideEmpty: Boolean? = null
|
||||||
): Response<DataContainer<CategoryListResponse>>
|
): Response<ListContainer<CategoryTemplate>>
|
||||||
|
|
||||||
@GET("outside/categories/tree")
|
@GET("outside/categories/tree")
|
||||||
suspend fun getCategoryTree(
|
suspend fun getCategoryTree(
|
||||||
@@ -668,14 +817,6 @@ interface RaveNowAPI {
|
|||||||
@Path("id") id: Int
|
@Path("id") id: Int
|
||||||
): Response<DataContainer<CategoryTemplate>>
|
): Response<DataContainer<CategoryTemplate>>
|
||||||
|
|
||||||
@GET("outside/prompts")
|
|
||||||
suspend fun getPromptsByCategory(
|
|
||||||
@Query("categoryIds") categoryIds: List<Int>? = null,
|
|
||||||
@Query("categoryName") categoryName: String? = null,
|
|
||||||
@Query("uncategorized") uncategorized: String? = null,
|
|
||||||
@Query("page") page: Int? = null,
|
|
||||||
@Query("pageSize") pageSize: Int? = null
|
|
||||||
): Response<ListContainer<Agent>>
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Row
|
|||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
@@ -28,19 +29,15 @@ import androidx.compose.foundation.pager.rememberPagerState
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.derivedStateOf
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
@@ -52,6 +49,10 @@ 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.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import androidx.compose.material.CircularProgressIndicator
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import com.aiosman.ravenow.AppStore
|
import com.aiosman.ravenow.AppStore
|
||||||
@@ -78,20 +79,18 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.lazy.grid.GridCells
|
import androidx.compose.ui.zIndex
|
||||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
|
||||||
import androidx.compose.foundation.lazy.grid.items as gridItems
|
|
||||||
|
|
||||||
// 检测是否接近列表底部的扩展函数
|
// 检测是否接近列表底部的扩展函数
|
||||||
fun LazyListState.isScrolledToEnd(buffer: Int = 3): Boolean {
|
fun LazyListState.isScrolledToEnd(buffer: Int = 3): Boolean {
|
||||||
val layoutInfo = this.layoutInfo
|
val layoutInfo = this.layoutInfo
|
||||||
val totalItemsCount = layoutInfo.totalItemsCount
|
val totalItemsCount = layoutInfo.totalItemsCount
|
||||||
val lastVisibleItemIndex = layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0
|
val lastVisibleItemIndex = layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0
|
||||||
|
|
||||||
return lastVisibleItemIndex >= (totalItemsCount - buffer)
|
return lastVisibleItemIndex >= (totalItemsCount - buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun Agent() {
|
fun Agent() {
|
||||||
val AppColors = LocalAppTheme.current
|
val AppColors = LocalAppTheme.current
|
||||||
@@ -111,6 +110,19 @@ fun Agent() {
|
|||||||
viewModel.ensureDataLoaded()
|
viewModel.ensureDataLoaded()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 列表滚动状态(用于吸顶与加载更多)
|
||||||
|
val listState = rememberLazyListState()
|
||||||
|
|
||||||
|
// 监听滚动到底部,加载更多网格数据
|
||||||
|
LaunchedEffect(listState) {
|
||||||
|
snapshotFlow { listState.isScrolledToEnd() }
|
||||||
|
.collect { atEnd ->
|
||||||
|
if (atEnd && !viewModel.isLoading) {
|
||||||
|
viewModel.loadMoreGridAgentData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 防抖状态
|
// 防抖状态
|
||||||
var lastClickTime by remember { mutableStateOf(0L) }
|
var lastClickTime by remember { mutableStateOf(0L) }
|
||||||
|
|
||||||
@@ -122,70 +134,61 @@ fun Agent() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val agentItems = viewModel.agentItems
|
Box(
|
||||||
var selectedTabIndex by remember { mutableStateOf(0) }
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
// 无限滚动状态
|
// 固定顶部搜索条
|
||||||
val listState = rememberLazyListState()
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
// 创建一个可观察的滚动到底部状态
|
.fillMaxWidth()
|
||||||
val isScrolledToEnd by remember {
|
.background(AppColors.background)
|
||||||
derivedStateOf {
|
.zIndex(999.0f)
|
||||||
listState.isScrolledToEnd()
|
.height(44.dp + statusBarPaddingValues.calculateTopPadding())
|
||||||
}
|
) {
|
||||||
}
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
// 检测滚动到底部并加载更多数据
|
.fillMaxWidth()
|
||||||
LaunchedEffect(isScrolledToEnd) {
|
.fillMaxHeight().padding(top = 32.dp, start = 16.dp, end = 16.dp),
|
||||||
if (isScrolledToEnd && !viewModel.isLoadingMore && agentItems.isNotEmpty() && viewModel.hasMoreData) {
|
horizontalArrangement = Arrangement.Start,
|
||||||
viewModel.loadMoreAgents()
|
verticalAlignment = Alignment.CenterVertically
|
||||||
}
|
) {
|
||||||
}
|
Text(
|
||||||
|
text = "Rave AI",
|
||||||
Scaffold(
|
fontSize = 20.sp,
|
||||||
topBar = {
|
fontWeight = FontWeight.W900,
|
||||||
TopAppBar(
|
color = AppColors.text,
|
||||||
title = {
|
modifier = Modifier
|
||||||
androidx.compose.material3.Text(
|
.align(Alignment.CenterVertically)
|
||||||
text = "Rave AI",
|
|
||||||
fontSize = 20.sp,
|
|
||||||
fontWeight = FontWeight.W900,
|
|
||||||
color = AppColors.text
|
|
||||||
)
|
|
||||||
},
|
|
||||||
actions = {
|
|
||||||
Image(
|
|
||||||
painter = painterResource(id = R.drawable.rider_pro_nav_search),
|
|
||||||
contentDescription = "search",
|
|
||||||
modifier = Modifier
|
|
||||||
.size(24.dp)
|
|
||||||
.noRippleClickable {
|
|
||||||
navController.navigate(NavigationRoute.Search.route)
|
|
||||||
},
|
|
||||||
colorFilter = ColorFilter.tint(AppColors.text)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
colors = TopAppBarDefaults.topAppBarColors(
|
|
||||||
containerColor = AppColors.background
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
},
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
containerColor = AppColors.background,
|
|
||||||
contentWindowInsets = WindowInsets(0, 0, 0, 0)
|
Image(
|
||||||
) { paddingValues ->
|
painter = painterResource(id = R.drawable.rider_pro_nav_search),
|
||||||
|
contentDescription = "search",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(24.dp)
|
||||||
|
.noRippleClickable {
|
||||||
|
navController.navigate(NavigationRoute.Search.route)
|
||||||
|
},
|
||||||
|
colorFilter = ColorFilter.tint(AppColors.text)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
state = listState,
|
state = listState,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(paddingValues)
|
|
||||||
.padding(
|
.padding(
|
||||||
|
top = 44.dp + statusBarPaddingValues.calculateTopPadding() + 15.dp,
|
||||||
bottom = navigationBarPaddings,
|
bottom = navigationBarPaddings,
|
||||||
start = 16.dp,
|
start = 16.dp,
|
||||||
end = 16.dp
|
end = 16.dp
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// 类别标签页 - 吸顶
|
// 动态标签页
|
||||||
stickyHeader(key = "category_tabs") {
|
stickyHeader(key = "category_tabs") {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -193,101 +196,33 @@ fun Agent() {
|
|||||||
.background(AppColors.background)
|
.background(AppColors.background)
|
||||||
.padding(top = 4.dp, bottom = 8.dp)
|
.padding(top = 4.dp, bottom = 8.dp)
|
||||||
) {
|
) {
|
||||||
LazyRow(
|
val selectedTabIndex = viewModel.selectedCategoryIndex
|
||||||
modifier = Modifier
|
if (viewModel.categories.isNotEmpty()) {
|
||||||
.fillMaxWidth()
|
LazyRow(
|
||||||
.wrapContentHeight()
|
modifier = Modifier
|
||||||
.padding(bottom = 8.dp),
|
.fillMaxWidth()
|
||||||
horizontalArrangement = Arrangement.Start,
|
.wrapContentHeight()
|
||||||
verticalAlignment = Alignment.CenterVertically
|
.padding(bottom = 16.dp),
|
||||||
) {
|
horizontalArrangement = Arrangement.Start,
|
||||||
item {
|
verticalAlignment = Alignment.CenterVertically
|
||||||
CustomTabItem(
|
) {
|
||||||
text = stringResource(R.string.agent_recommend),
|
viewModel.categories.forEachIndexed { index, category ->
|
||||||
isSelected = selectedTabIndex == 0,
|
item {
|
||||||
onClick = {
|
CustomTabItem(
|
||||||
selectedTabIndex = 0
|
text = category.getLocalizedName(),
|
||||||
viewModel.loadAllAgents()
|
isSelected = selectedTabIndex == index,
|
||||||
|
onClick = { viewModel.selectCategory(index) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (index < viewModel.categories.size - 1) {
|
||||||
|
item { TabSpacer() }
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
item {
|
|
||||||
TabSpacer()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 动态添加分类标签
|
|
||||||
viewModel.categories.take(4).forEachIndexed { index, category ->
|
|
||||||
item {
|
|
||||||
CustomTabItem(
|
|
||||||
text = category.name,
|
|
||||||
isSelected = selectedTabIndex == index + 1,
|
|
||||||
onClick = {
|
|
||||||
selectedTabIndex = index + 1
|
|
||||||
viewModel.loadAgentsByCategory(category.id)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
item {
|
|
||||||
TabSpacer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
item {
|
|
||||||
CustomTabItem(
|
|
||||||
text = "scenes",
|
|
||||||
isSelected = selectedTabIndex == 1,
|
|
||||||
onClick = {
|
|
||||||
selectedTabIndex = 1
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
item {
|
|
||||||
TabSpacer()
|
|
||||||
}
|
|
||||||
|
|
||||||
item {
|
|
||||||
CustomTabItem(
|
|
||||||
text = "voices",
|
|
||||||
isSelected = selectedTabIndex == 6,
|
|
||||||
onClick = {
|
|
||||||
selectedTabIndex = 6
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
item {
|
|
||||||
TabSpacer()
|
|
||||||
}
|
|
||||||
|
|
||||||
item {
|
|
||||||
CustomTabItem(
|
|
||||||
text = "anime",
|
|
||||||
isSelected = selectedTabIndex == 7,
|
|
||||||
onClick = {
|
|
||||||
selectedTabIndex = 7
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
item {
|
|
||||||
TabSpacer()
|
|
||||||
}
|
|
||||||
|
|
||||||
item {
|
|
||||||
CustomTabItem(
|
|
||||||
text = "assist",
|
|
||||||
isSelected = selectedTabIndex == 8,
|
|
||||||
onClick = {
|
|
||||||
selectedTabIndex = 8
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 推荐内容区域
|
// 推荐内容区域
|
||||||
item {
|
item {
|
||||||
Column(
|
Column(
|
||||||
@@ -295,23 +230,59 @@ fun Agent() {
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(vertical = 8.dp)
|
.padding(vertical = 8.dp)
|
||||||
) {
|
) {
|
||||||
when {
|
AgentViewPagerSection(agentItems = viewModel.topAgentItems, viewModel)
|
||||||
selectedTabIndex == 0 -> {
|
}
|
||||||
AgentViewPagerSection(agentItems = viewModel.agentItems.take(15), viewModel)
|
}
|
||||||
}
|
|
||||||
selectedTabIndex in 1..viewModel.categories.size -> {
|
// 热门聊天室
|
||||||
AgentViewPagerSection(agentItems = viewModel.agentItems.take(15), viewModel)
|
stickyHeader(key = "hot_rooms_header") {
|
||||||
}
|
Row(
|
||||||
else -> {
|
modifier = Modifier
|
||||||
val shuffledAgents = viewModel.agentItems.shuffled().take(15)
|
.fillMaxWidth()
|
||||||
AgentViewPagerSection(agentItems = shuffledAgents, viewModel)
|
.background(AppColors.background)
|
||||||
}
|
.padding(top = 8.dp, bottom = 12.dp),
|
||||||
|
horizontalArrangement = Arrangement.Start,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.mipmap.rider_pro_hot_room),
|
||||||
|
contentDescription = "chat room",
|
||||||
|
modifier = Modifier.size(28.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
androidx.compose.material3.Text(
|
||||||
|
text = stringResource(R.string.hot_rooms),
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W900,
|
||||||
|
color = AppColors.text
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 热门聊天室网格
|
||||||
|
items(viewModel.chatRooms.chunked(2)) { rowRooms ->
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 12.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
|
) {
|
||||||
|
rowRooms.forEach { chatRoom ->
|
||||||
|
ChatRoomCard(
|
||||||
|
chatRoom = chatRoom,
|
||||||
|
navController = LocalNavController.current,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (rowRooms.size == 1) {
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item { Spacer(modifier = Modifier.height(20.dp)) }
|
||||||
|
|
||||||
// "发现更多" 标题 - 吸顶
|
// 发现区域
|
||||||
stickyHeader(key = "discover_more") {
|
stickyHeader(key = "discover_more") {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -322,9 +293,9 @@ fun Agent() {
|
|||||||
verticalAlignment = Alignment.Bottom
|
verticalAlignment = Alignment.Bottom
|
||||||
) {
|
) {
|
||||||
Image(
|
Image(
|
||||||
painter = painterResource(R.mipmap.rider_pro_agent2),
|
painter = painterResource(R.mipmap.bars_x_buttons_home_n_copy_2),
|
||||||
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))
|
||||||
androidx.compose.material3.Text(
|
androidx.compose.material3.Text(
|
||||||
@@ -335,38 +306,42 @@ fun Agent() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
item { Spacer(modifier = Modifier.height(20.dp)) }
|
||||||
|
|
||||||
// Agent网格 - 使用行式布局
|
// Agent 两列网格行
|
||||||
items(
|
items(viewModel.gridAgentItems.chunked(2)) { rowItems ->
|
||||||
items = agentItems.chunked(2),
|
|
||||||
key = { row -> row.firstOrNull()?.openId ?: "" }
|
|
||||||
) { rowItems ->
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(vertical = 16.dp),
|
.padding(
|
||||||
|
top = 20.dp,
|
||||||
|
bottom = 20.dp
|
||||||
|
),
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
rowItems.forEach { agentItem ->
|
Box(modifier = Modifier.weight(1f)) {
|
||||||
Box(
|
AgentCardSquare(
|
||||||
modifier = Modifier.weight(1f)
|
agentItem = rowItems[0],
|
||||||
) {
|
viewModel = viewModel,
|
||||||
|
navController = LocalNavController.current
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (rowItems.size > 1) {
|
||||||
|
Box(modifier = Modifier.weight(1f)) {
|
||||||
AgentCardSquare(
|
AgentCardSquare(
|
||||||
agentItem = agentItem,
|
agentItem = rowItems[1],
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
navController = LocalNavController.current
|
navController = LocalNavController.current
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
// 如果这一行只有一个item,添加一个空的占位符
|
|
||||||
if (rowItems.size == 1) {
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载更多指示器
|
// 加载更多指示器
|
||||||
if (viewModel.isLoadingMore) {
|
if (viewModel.isLoading) {
|
||||||
item {
|
item {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -381,7 +356,7 @@ fun Agent() {
|
|||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(12.dp))
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
androidx.compose.material3.Text(
|
androidx.compose.material3.Text(
|
||||||
text = "加载中...",
|
text = stringResource(R.string.agent_chat_loading),
|
||||||
color = AppColors.secondaryText,
|
color = AppColors.secondaryText,
|
||||||
fontSize = 14.sp
|
fontSize = 14.sp
|
||||||
)
|
)
|
||||||
@@ -391,11 +366,67 @@ fun Agent() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AgentGridLayout(
|
||||||
|
agentItems: List<AgentItem>,
|
||||||
|
viewModel: AgentViewModel,
|
||||||
|
navController: NavHostController
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
// 将agentItems按两列分组
|
||||||
|
agentItems.chunked(2).forEachIndexed { rowIndex, rowItems ->
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(
|
||||||
|
top = if (rowIndex == 0) 30.dp else 20.dp, // 第一行添加更多顶部间距
|
||||||
|
bottom = 20.dp
|
||||||
|
),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
// 第一列
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
) {
|
||||||
|
AgentCardSquare(
|
||||||
|
agentItem = rowItems[0],
|
||||||
|
viewModel = viewModel,
|
||||||
|
navController = navController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二列(如果存在)
|
||||||
|
if (rowItems.size > 1) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
) {
|
||||||
|
AgentCardSquare(
|
||||||
|
agentItem = rowItems[1],
|
||||||
|
viewModel = viewModel,
|
||||||
|
navController = navController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果只有一列,添加空白占位
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("SuspiciousIndentation")
|
@SuppressLint("SuspiciousIndentation")
|
||||||
@Composable
|
@Composable
|
||||||
fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navController: NavHostController) {
|
fun AgentCardSquare(
|
||||||
|
agentItem: AgentItem,
|
||||||
|
viewModel: AgentViewModel,
|
||||||
|
navController: NavHostController
|
||||||
|
) {
|
||||||
val AppColors = LocalAppTheme.current
|
val AppColors = LocalAppTheme.current
|
||||||
val cardHeight = 200.dp
|
val cardHeight = 180.dp
|
||||||
val avatarSize = cardHeight / 3 // 头像大小为方块高度的三分之一
|
val avatarSize = cardHeight / 3 // 头像大小为方块高度的三分之一
|
||||||
|
|
||||||
// 防抖状态
|
// 防抖状态
|
||||||
@@ -404,9 +435,8 @@ fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navControll
|
|||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = avatarSize / 2)
|
|
||||||
.height(cardHeight)
|
.height(cardHeight)
|
||||||
.background(AppColors.nonActive, RoundedCornerShape(12.dp)) // 修改背景颜色
|
.background(AppColors.secondaryBackground, RoundedCornerShape(12.dp))
|
||||||
.clickable {
|
.clickable {
|
||||||
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
|
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
|
||||||
viewModel.goToProfile(agentItem.openId, navController)
|
viewModel.goToProfile(agentItem.openId, navController)
|
||||||
@@ -416,12 +446,11 @@ fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navControll
|
|||||||
},
|
},
|
||||||
contentAlignment = Alignment.TopCenter
|
contentAlignment = Alignment.TopCenter
|
||||||
) {
|
) {
|
||||||
// 头像,位于方块上方居中,部分悬于方块外部
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.offset(y = -avatarSize / 2)
|
.offset(y = 4.dp)
|
||||||
.size(avatarSize)
|
.size(avatarSize)
|
||||||
.background(Color.White, RoundedCornerShape(avatarSize / 2))
|
.background(AppColors.background, RoundedCornerShape(avatarSize / 2))
|
||||||
.clip(RoundedCornerShape(avatarSize / 2)),
|
.clip(RoundedCornerShape(avatarSize / 2)),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
@@ -429,9 +458,7 @@ fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navControll
|
|||||||
painter = painterResource(R.mipmap.group_copy),
|
painter = painterResource(R.mipmap.group_copy),
|
||||||
contentDescription = "默认头像",
|
contentDescription = "默认头像",
|
||||||
modifier = Modifier.size(avatarSize),
|
modifier = Modifier.size(avatarSize),
|
||||||
contentScale = androidx.compose.ui.layout.ContentScale.Crop
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (agentItem.avatar.isNotEmpty()) {
|
if (agentItem.avatar.isNotEmpty()) {
|
||||||
CustomAsyncImage(
|
CustomAsyncImage(
|
||||||
imageUrl = agentItem.avatar,
|
imageUrl = agentItem.avatar,
|
||||||
@@ -448,7 +475,7 @@ fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navControll
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = avatarSize / 2 + 8.dp, start = 8.dp, end = 8.dp, bottom = 8.dp),
|
.padding(top = 4.dp + avatarSize + 8.dp, start = 8.dp, end = 8.dp, bottom = 48.dp), // 为底部聊天按钮留出空间
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
androidx.compose.material3.Text(
|
androidx.compose.material3.Text(
|
||||||
@@ -462,62 +489,63 @@ fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navControll
|
|||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
Box(
|
androidx.compose.material3.Text(
|
||||||
modifier = Modifier
|
text = agentItem.desc,
|
||||||
.height(85.dp)
|
fontSize = 12.sp,
|
||||||
.fillMaxWidth()
|
color = AppColors.secondaryText,
|
||||||
) {
|
maxLines = 2,
|
||||||
androidx.compose.material3.Text(
|
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
|
||||||
text = agentItem.desc,
|
modifier = Modifier.weight(1f, fill = false)
|
||||||
fontSize = 12.sp,
|
)
|
||||||
color = AppColors.secondaryText,
|
}
|
||||||
maxLines = 5,
|
|
||||||
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
// 聊天按钮,固定在底部居中,距离底部有一定边距
|
||||||
|
Box(
|
||||||
// 聊天按钮,位于底部居中
|
modifier = Modifier
|
||||||
Box(
|
.align(Alignment.BottomCenter)
|
||||||
modifier = Modifier
|
.padding(bottom = 12.dp) // 距离底部的边距
|
||||||
.width(60.dp)
|
.width(60.dp)
|
||||||
.height(32.dp)
|
.height(32.dp)
|
||||||
.background(
|
.background(
|
||||||
color = Color(0X147c7480),
|
|
||||||
shape = RoundedCornerShape(8.dp)
|
|
||||||
)
|
|
||||||
.clickable {
|
|
||||||
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
|
|
||||||
// 检查游客模式,如果是游客则跳转登录
|
|
||||||
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.CHAT_WITH_AGENT)) {
|
|
||||||
navController.navigate(NavigationRoute.Login.route)
|
|
||||||
} else {
|
|
||||||
viewModel.createSingleChat(agentItem.openId)
|
|
||||||
viewModel.goToChatAi(
|
|
||||||
agentItem.openId,
|
|
||||||
navController = navController
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
lastClickTime = System.currentTimeMillis()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
androidx.compose.material3.Text(
|
|
||||||
text = stringResource(R.string.chat),
|
|
||||||
fontSize = 12.sp,
|
|
||||||
color = AppColors.text,
|
color = AppColors.text,
|
||||||
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
|
shape = RoundedCornerShape(
|
||||||
|
topStart = 10.dp,
|
||||||
|
topEnd = 10.dp,
|
||||||
|
bottomStart = 0.dp,
|
||||||
|
bottomEnd = 10.dp
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
.clickable {
|
||||||
|
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
|
||||||
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.CHAT_WITH_AGENT)) {
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
} else {
|
||||||
|
viewModel.createSingleChat(agentItem.openId)
|
||||||
|
viewModel.goToChatAi(
|
||||||
|
agentItem.openId,
|
||||||
|
navController = navController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
lastClickTime = System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
androidx.compose.material3.Text(
|
||||||
|
text = stringResource(R.string.chat),
|
||||||
|
fontSize = 15.sp,
|
||||||
|
color = AppColors.background,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AgentViewPagerSection(agentItems: List<AgentItem>,viewModel: AgentViewModel) {
|
fun AgentViewPagerSection(agentItems: List<AgentItem>, viewModel: AgentViewModel) {
|
||||||
val AppColors = LocalAppTheme.current
|
val AppColors = LocalAppTheme.current
|
||||||
|
|
||||||
// 每页显示5个agent
|
// 每页显示5个agent
|
||||||
@@ -531,7 +559,7 @@ fun AgentViewPagerSection(agentItems: List<AgentItem>,viewModel: AgentViewModel)
|
|||||||
// Agent内容
|
// Agent内容
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(310.dp)
|
.height(300.dp)
|
||||||
) {
|
) {
|
||||||
HorizontalPager(
|
HorizontalPager(
|
||||||
state = pagerState,
|
state = pagerState,
|
||||||
@@ -553,7 +581,7 @@ fun AgentViewPagerSection(agentItems: List<AgentItem>,viewModel: AgentViewModel)
|
|||||||
agentItems = agentItems.drop(page * itemsPerPage).take(itemsPerPage),
|
agentItems = agentItems.drop(page * itemsPerPage).take(itemsPerPage),
|
||||||
page = page,
|
page = page,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(310.dp)
|
.height(300.dp)
|
||||||
.graphicsLayer {
|
.graphicsLayer {
|
||||||
scaleX = scale
|
scaleX = scale
|
||||||
scaleY = scale
|
scaleY = scale
|
||||||
@@ -590,7 +618,13 @@ fun AgentViewPagerSection(agentItems: List<AgentItem>,viewModel: AgentViewModel)
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AgentPage(viewModel: AgentViewModel,agentItems: List<AgentItem>, page: Int, modifier: Modifier = Modifier,navController: NavHostController) {
|
fun AgentPage(
|
||||||
|
viewModel: AgentViewModel,
|
||||||
|
agentItems: List<AgentItem>,
|
||||||
|
page: Int,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
navController: NavHostController
|
||||||
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -598,7 +632,11 @@ fun AgentPage(viewModel: AgentViewModel,agentItems: List<AgentItem>, page: Int,
|
|||||||
) {
|
) {
|
||||||
// 显示3个agent
|
// 显示3个agent
|
||||||
agentItems.forEachIndexed { index, agentItem ->
|
agentItems.forEachIndexed { index, agentItem ->
|
||||||
AgentCard2(agentItem = agentItem, viewModel = viewModel, navController = LocalNavController.current)
|
AgentCard2(
|
||||||
|
agentItem = agentItem,
|
||||||
|
viewModel = viewModel,
|
||||||
|
navController = LocalNavController.current
|
||||||
|
)
|
||||||
if (index < agentItems.size - 1) {
|
if (index < agentItems.size - 1) {
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
}
|
}
|
||||||
@@ -608,9 +646,9 @@ fun AgentPage(viewModel: AgentViewModel,agentItems: List<AgentItem>, page: Int,
|
|||||||
|
|
||||||
@SuppressLint("SuspiciousIndentation")
|
@SuppressLint("SuspiciousIndentation")
|
||||||
@Composable
|
@Composable
|
||||||
fun AgentCard2(viewModel: AgentViewModel,agentItem: AgentItem,navController: NavHostController) {
|
fun AgentCard2(viewModel: AgentViewModel, agentItem: AgentItem, navController: NavHostController) {
|
||||||
val AppColors = LocalAppTheme.current
|
val AppColors = LocalAppTheme.current
|
||||||
|
|
||||||
// 防抖状态
|
// 防抖状态
|
||||||
var lastClickTime by remember { mutableStateOf(0L) }
|
var lastClickTime by remember { mutableStateOf(0L) }
|
||||||
|
|
||||||
@@ -624,7 +662,7 @@ fun AgentCard2(viewModel: AgentViewModel,agentItem: AgentItem,navController: Nav
|
|||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(48.dp)
|
.size(48.dp)
|
||||||
.background(Color(0x00F5F5F5), RoundedCornerShape(24.dp))
|
.background(AppColors.secondaryBackground, RoundedCornerShape(24.dp))
|
||||||
.clickable {
|
.clickable {
|
||||||
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
|
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
|
||||||
viewModel.goToProfile(agentItem.openId, navController)
|
viewModel.goToProfile(agentItem.openId, navController)
|
||||||
@@ -634,12 +672,6 @@ fun AgentCard2(viewModel: AgentViewModel,agentItem: AgentItem,navController: Nav
|
|||||||
},
|
},
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Image(
|
|
||||||
painter = painterResource(R.mipmap.group_copy),
|
|
||||||
contentDescription = "默认头像",
|
|
||||||
modifier = Modifier.size(48.dp),
|
|
||||||
)
|
|
||||||
|
|
||||||
if (agentItem.avatar.isNotEmpty()) {
|
if (agentItem.avatar.isNotEmpty()) {
|
||||||
CustomAsyncImage(
|
CustomAsyncImage(
|
||||||
imageUrl = agentItem.avatar,
|
imageUrl = agentItem.avatar,
|
||||||
@@ -649,6 +681,13 @@ fun AgentCard2(viewModel: AgentViewModel,agentItem: AgentItem,navController: Nav
|
|||||||
.clip(RoundedCornerShape(24.dp)),
|
.clip(RoundedCornerShape(24.dp)),
|
||||||
contentScale = androidx.compose.ui.layout.ContentScale.Crop
|
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)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -687,7 +726,7 @@ fun AgentCard2(viewModel: AgentViewModel,agentItem: AgentItem,navController: Nav
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(width = 60.dp, height = 32.dp)
|
.size(width = 60.dp, height = 32.dp)
|
||||||
.background(
|
.background(
|
||||||
color = Color(0X147c7480),
|
color = AppColors.inputBackground,
|
||||||
shape = RoundedCornerShape(8.dp)
|
shape = RoundedCornerShape(8.dp)
|
||||||
)
|
)
|
||||||
.clickable {
|
.clickable {
|
||||||
@@ -717,3 +756,180 @@ fun AgentCard2(viewModel: AgentViewModel,agentItem: AgentItem,navController: Nav
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ChatRoomsSection(
|
||||||
|
chatRooms: List<ChatRoom>,
|
||||||
|
navController: NavHostController
|
||||||
|
) {
|
||||||
|
val AppColors = LocalAppTheme.current
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
// 标题
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 16.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.mipmap.rider_pro_hot_room),
|
||||||
|
contentDescription = "chat room",
|
||||||
|
modifier = Modifier.size(28.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
androidx.compose.material3.Text(
|
||||||
|
text = stringResource(R.string.hot_rooms),
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W900,
|
||||||
|
color = AppColors.text
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
chatRooms.chunked(2).forEach { rowRooms ->
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 12.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
|
) {
|
||||||
|
rowRooms.forEach { chatRoom ->
|
||||||
|
ChatRoomCard(
|
||||||
|
chatRoom = chatRoom,
|
||||||
|
navController = navController,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ChatRoomCard(
|
||||||
|
chatRoom: ChatRoom,
|
||||||
|
navController: NavHostController,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val AppColors = LocalAppTheme.current
|
||||||
|
val cardSize = 160.dp
|
||||||
|
val viewModel: AgentViewModel = viewModel()
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
// 防抖状态
|
||||||
|
var lastClickTime by remember { mutableStateOf(0L) }
|
||||||
|
|
||||||
|
// Loading 对话框
|
||||||
|
if (viewModel.isJoiningRoom) {
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = { /* 阻止用户关闭对话框 */ },
|
||||||
|
properties = DialogProperties(
|
||||||
|
dismissOnBackPress = false,
|
||||||
|
dismissOnClickOutside = false
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(120.dp)
|
||||||
|
.background(
|
||||||
|
color = AppColors.background,
|
||||||
|
shape = RoundedCornerShape(12.dp)
|
||||||
|
),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier.size(32.dp),
|
||||||
|
color = AppColors.main
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
androidx.compose.material3.Text(
|
||||||
|
text = "加入中...",
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = AppColors.text
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正方形卡片,文字重叠在底部
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.size(cardSize)
|
||||||
|
.background(AppColors.secondaryBackground, RoundedCornerShape(12.dp))
|
||||||
|
.clickable(enabled = !viewModel.isJoiningRoom) {
|
||||||
|
if (!viewModel.isJoiningRoom && DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
|
||||||
|
// 加入群聊房间
|
||||||
|
viewModel.joinRoom(
|
||||||
|
id = chatRoom.id,
|
||||||
|
name = chatRoom.name,
|
||||||
|
avatar = chatRoom.avatar,
|
||||||
|
context = context,
|
||||||
|
navController = navController,
|
||||||
|
onSuccess = {
|
||||||
|
// 成功加入房间
|
||||||
|
},
|
||||||
|
onError = { errorMsg ->
|
||||||
|
// 处理错误,可以显示Toast或其他提示
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
lastClickTime = System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
// 优先显示banner,如果没有banner则显示头像
|
||||||
|
val imageUrl = if (chatRoom.banner.isNotEmpty()) chatRoom.banner else chatRoom.avatar
|
||||||
|
|
||||||
|
if (imageUrl.isNotEmpty()) {
|
||||||
|
CustomAsyncImage(
|
||||||
|
imageUrl = imageUrl,
|
||||||
|
contentDescription = if (chatRoom.banner.isNotEmpty()) "房间banner" else "房间头像",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(cardSize)
|
||||||
|
.clip(RoundedCornerShape(12.dp)),
|
||||||
|
contentScale = androidx.compose.ui.layout.ContentScale.Crop
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// 默认房间图标
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.mipmap.rider_pro_agent),
|
||||||
|
contentDescription = "默认房间图标",
|
||||||
|
modifier = Modifier.size(cardSize * 0.4f),
|
||||||
|
colorFilter = ColorFilter.tint(AppColors.secondaryText)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 房间名称,重叠在底部
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.BottomCenter)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(
|
||||||
|
color = Color.Black.copy(alpha = 0.6f),
|
||||||
|
shape = RoundedCornerShape(bottomStart = 12.dp, bottomEnd = 12.dp)
|
||||||
|
)
|
||||||
|
.padding(horizontal = 8.dp, vertical = 6.dp)
|
||||||
|
) {
|
||||||
|
androidx.compose.material3.Text(
|
||||||
|
text = chatRoom.name,
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = Color.White,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
textAlign = androidx.compose.ui.text.style.TextAlign.Center
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,119 +1,220 @@
|
|||||||
package com.aiosman.ravenow.ui.index.tabs.ai
|
package com.aiosman.ravenow.ui.index.tabs.ai
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
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 androidx.navigation.NavHostController
|
||||||
import com.aiosman.ravenow.data.Agent
|
import com.aiosman.ravenow.AppState
|
||||||
import com.aiosman.ravenow.data.ListContainer
|
import com.aiosman.ravenow.AppStore
|
||||||
|
import com.aiosman.ravenow.ConstVars
|
||||||
|
import com.aiosman.ravenow.data.Room
|
||||||
import com.aiosman.ravenow.data.api.ApiClient
|
import com.aiosman.ravenow.data.api.ApiClient
|
||||||
import com.aiosman.ravenow.data.api.CategoryTemplate
|
import com.aiosman.ravenow.data.api.CategoryTemplate
|
||||||
|
import com.aiosman.ravenow.data.api.CreateGroupChatRequestBody
|
||||||
import com.aiosman.ravenow.data.api.RaveNowAPI
|
import com.aiosman.ravenow.data.api.RaveNowAPI
|
||||||
import com.aiosman.ravenow.data.api.SingleChatRequestBody
|
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.ai.tabs.mine.MineAgentViewModel.createGroup2ChatAi
|
||||||
import com.aiosman.ravenow.ui.NavigationRoute
|
import com.aiosman.ravenow.ui.NavigationRoute
|
||||||
import com.aiosman.ravenow.ui.index.tabs.message.MessageListViewModel.userService
|
import com.aiosman.ravenow.ui.index.tabs.message.MessageListViewModel.userService
|
||||||
|
import com.aiosman.ravenow.ui.index.tabs.message.tab.GroupChatListViewModel.createGroupChat
|
||||||
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre.AgentItem
|
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre.AgentItem
|
||||||
|
import com.aiosman.ravenow.ui.navigateToGroupChat
|
||||||
|
import com.aiosman.ravenow.data.api.ApiErrorResponse
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import android.content.Context
|
||||||
|
import android.widget.Toast
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
object AgentViewModel: ViewModel() {
|
data class ChatRoom(
|
||||||
|
val id: Int,
|
||||||
|
val name: String,
|
||||||
|
val avatar: String = "",
|
||||||
|
val banner: String = "",
|
||||||
|
val memberCount: Int = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
object AgentViewModel : ViewModel() {
|
||||||
|
|
||||||
private val apiClient: RaveNowAPI = ApiClient.api
|
private val apiClient: RaveNowAPI = ApiClient.api
|
||||||
|
|
||||||
var agentItems by mutableStateOf<List<AgentItem>>(emptyList())
|
// 顶部Agent列表数据(用于ViewPager)
|
||||||
|
var topAgentItems by mutableStateOf<List<AgentItem>>(emptyList())
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var categories by mutableStateOf<List<CategoryItem>>(emptyList())
|
// 底部网格布局Agent数据
|
||||||
|
var gridAgentItems by mutableStateOf<List<AgentItem>>(emptyList())
|
||||||
|
private set
|
||||||
|
|
||||||
|
var chatRooms by mutableStateOf<List<ChatRoom>>(emptyList())
|
||||||
|
private set
|
||||||
|
|
||||||
|
var rooms by mutableStateOf<List<Room>>(emptyList())
|
||||||
|
private set
|
||||||
|
|
||||||
|
var categories by mutableStateOf<List<CategoryTemplate>>(emptyList())
|
||||||
|
private set
|
||||||
|
|
||||||
|
var selectedCategoryIndex by mutableStateOf(0)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var errorMessage by mutableStateOf<String?>(null)
|
var errorMessage by mutableStateOf<String?>(null)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
|
||||||
var isRefreshing by mutableStateOf(false)
|
var isRefreshing by mutableStateOf(false)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var isLoading by mutableStateOf(false)
|
var isLoading by mutableStateOf(false)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
// 分页相关状态
|
var isJoiningRoom by mutableStateOf(false)
|
||||||
var isLoadingMore by mutableStateOf(false)
|
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var currentPage by mutableStateOf(1)
|
private var topCurrentPage = 1
|
||||||
private set
|
private var topHasMoreData = true
|
||||||
|
|
||||||
var hasMoreData by mutableStateOf(true)
|
private var gridCurrentPage = 1
|
||||||
private set
|
private var gridHasMoreData = true
|
||||||
|
|
||||||
private val pageSize = 20
|
|
||||||
private var currentCategoryId: Int? = null
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
loadAgentData()
|
loadTopAgentData()
|
||||||
|
loadGridAgentData()
|
||||||
|
loadChatRooms()
|
||||||
loadCategories()
|
loadCategories()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadAgentData(categoryId: Int? = null, page: Int = 1, isLoadMore: Boolean = false) {
|
/**
|
||||||
|
* 加载顶部Agent列表数据(用于ViewPager)
|
||||||
|
*/
|
||||||
|
private fun loadTopAgentData(categoryIndex: Int = 0) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
if (isLoadMore) {
|
isLoading = true
|
||||||
isLoadingMore = true
|
|
||||||
} else {
|
|
||||||
isLoading = true
|
|
||||||
// 重置分页状态
|
|
||||||
currentPage = 1
|
|
||||||
hasMoreData = true
|
|
||||||
currentCategoryId = categoryId
|
|
||||||
}
|
|
||||||
|
|
||||||
errorMessage = null
|
errorMessage = null
|
||||||
|
topCurrentPage = 1
|
||||||
|
topHasMoreData = true
|
||||||
try {
|
try {
|
||||||
val response = if (categoryId != null) {
|
val selectedCategory =
|
||||||
// 根据分类ID获取智能体
|
if (categoryIndex < categories.size) categories[categoryIndex] else null
|
||||||
|
|
||||||
|
val response = if (categoryIndex == 0 || selectedCategory == null) {
|
||||||
|
// 推荐分类或无效分类,加载所有 Agent
|
||||||
apiClient.getAgent(
|
apiClient.getAgent(
|
||||||
page = page,
|
page = topCurrentPage,
|
||||||
pageSize = pageSize,
|
pageSize = 15,
|
||||||
withWorkflow = 1,
|
withWorkflow = "1",
|
||||||
categoryIds = listOf(categoryId)
|
random = "1"
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// 获取所有智能体
|
// 特定分类,使用 categoryName 参数
|
||||||
apiClient.getAgent(page = page, pageSize = pageSize, withWorkflow = 1)
|
apiClient.getAgent(
|
||||||
|
page = topCurrentPage,
|
||||||
|
pageSize = 15,
|
||||||
|
withWorkflow = "1",
|
||||||
|
categoryName = selectedCategory.name,
|
||||||
|
random = "1"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
val responseData = response.body()?.data
|
val agents = response.body()?.data?.list ?: emptyList()
|
||||||
val agents = responseData?.list ?: emptyList<Agent>()
|
topAgentItems = agents.map { agent ->
|
||||||
val newAgentItems = agents.map { agent ->
|
|
||||||
AgentItem.fromAgent(agent)
|
AgentItem.fromAgent(agent)
|
||||||
}
|
}
|
||||||
|
topHasMoreData = agents.size >= 15
|
||||||
if (isLoadMore) {
|
|
||||||
// 加载更多:追加到现有列表
|
|
||||||
agentItems = agentItems + newAgentItems
|
|
||||||
currentPage = page
|
|
||||||
} else {
|
|
||||||
// 首次加载或刷新:替换整个列表
|
|
||||||
agentItems = newAgentItems
|
|
||||||
currentPage = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否还有更多数据
|
|
||||||
hasMoreData = agents.size >= pageSize
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
errorMessage = "获取Agent数据失败: ${response.code()}"
|
errorMessage = "获取顶部Agent数据失败: ${response.code()}"
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
errorMessage = "网络请求失败: ${e.message}"
|
errorMessage = "网络请求失败: ${e.message}"
|
||||||
} finally {
|
} finally {
|
||||||
if (isLoadMore) {
|
isLoading = false
|
||||||
isLoadingMore = false
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载底部网格布局Agent数据
|
||||||
|
*/
|
||||||
|
private fun loadGridAgentData(categoryIndex: Int = 0) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
isLoading = true
|
||||||
|
errorMessage = null
|
||||||
|
gridCurrentPage = 1
|
||||||
|
gridHasMoreData = true
|
||||||
|
try {
|
||||||
|
val selectedCategory =
|
||||||
|
if (categoryIndex < categories.size) categories[categoryIndex] else null
|
||||||
|
|
||||||
|
val response = if (categoryIndex == 0 || selectedCategory == null) {
|
||||||
|
// 推荐分类或无效分类,加载所有 Agent
|
||||||
|
apiClient.getAgent(
|
||||||
|
page = gridCurrentPage,
|
||||||
|
pageSize = 20,
|
||||||
|
withWorkflow = "1",
|
||||||
|
random = "true"
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
isLoading = false
|
// 特定分类,使用 categoryName 参数
|
||||||
|
apiClient.getAgent(
|
||||||
|
page = gridCurrentPage,
|
||||||
|
pageSize = 20,
|
||||||
|
withWorkflow = "1",
|
||||||
|
categoryName = selectedCategory.name,
|
||||||
|
random = "true"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
val agents = response.body()?.data?.list ?: emptyList()
|
||||||
|
gridAgentItems = agents.map { agent ->
|
||||||
|
AgentItem.fromAgent(agent)
|
||||||
|
}
|
||||||
|
gridHasMoreData = agents.size >= 20
|
||||||
|
} else {
|
||||||
|
errorMessage = "获取网格Agent数据失败: ${response.code()}"
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
errorMessage = "网络请求失败: ${e.message}"
|
||||||
|
} finally {
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadChatRooms() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val response = apiClient.getRooms(
|
||||||
|
page = 1,
|
||||||
|
pageSize = 21,
|
||||||
|
isRecommended = 1,
|
||||||
|
random = 1
|
||||||
|
) // 请求21个,确保是3的倍数
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
val allRooms = response.body()?.list ?: emptyList()
|
||||||
|
// 确保房间数量是3的倍数,如果不足则截取,如果超出则取前几个
|
||||||
|
val targetCount = (allRooms.size / 3) * 3 // 向下取整到最近的3的倍数
|
||||||
|
rooms = allRooms.take(targetCount)
|
||||||
|
|
||||||
|
// 转换为ChatRoom格式用于兼容现有UI
|
||||||
|
chatRooms = rooms.map { room ->
|
||||||
|
ChatRoom(
|
||||||
|
id = room.id,
|
||||||
|
name = room.name,
|
||||||
|
avatar = room.avatar,
|
||||||
|
banner = ConstVars.BASE_SERVER + "/api/v1/outside/" + room.recommendBanner + "?token=${AppStore.token}",
|
||||||
|
memberCount = room.userCount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// 如果网络请求失败,使用默认数据
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,72 +222,137 @@ object AgentViewModel: ViewModel() {
|
|||||||
private fun loadCategories() {
|
private fun loadCategories() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
val response = apiClient.getCategories(
|
val categoriesResponse = apiClient.getCategories(
|
||||||
pageSize = 20,
|
page = 1,
|
||||||
withChildren = false,
|
pageSize = 100,
|
||||||
withParent = false,
|
|
||||||
withCount = true,
|
|
||||||
hideEmpty = true
|
|
||||||
)
|
)
|
||||||
println("分类数据请求完成,响应成功: ${response.isSuccessful}")
|
if (categoriesResponse.isSuccessful && categoriesResponse.body() != null) {
|
||||||
if (response.isSuccessful) {
|
// 添加一个默认的"推荐"分类在第一位
|
||||||
val categoryList = response.body()?.data?.list ?: emptyList()
|
val recommendCategory = createRecommendCategory()
|
||||||
println("获取到 ${categoryList.size} 个分类")
|
val categoriesList = categoriesResponse.body()?.list ?: emptyList()
|
||||||
categories = categoryList.map { category ->
|
categories = listOf(recommendCategory) + categoriesList
|
||||||
CategoryItem.fromCategoryTemplate(category)
|
|
||||||
|
// 分类加载完成后,重新加载当前选中分类的 Agent 数据
|
||||||
|
if (topAgentItems.isEmpty()) {
|
||||||
|
loadTopAgentData(selectedCategoryIndex)
|
||||||
|
}
|
||||||
|
if (gridAgentItems.isEmpty()) {
|
||||||
|
loadGridAgentData(selectedCategoryIndex)
|
||||||
}
|
}
|
||||||
println("成功处理并映射了 ${categories.size} 个分类")
|
|
||||||
} else {
|
} else {
|
||||||
errorMessage = "获取分类数据失败: ${response.code()}"
|
// 如果请求失败,使用默认分类
|
||||||
println("获取分类数据失败: ${response.code()}")
|
categories = listOf(createRecommendCategory())
|
||||||
|
errorMessage = "获取分类失败: ${categoriesResponse.code()}"
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
errorMessage = "获取分类数据失败: ${e.message}"
|
// 如果网络请求失败,使用默认分类
|
||||||
println("获取分类数据异常: ${e.message}")
|
categories = listOf(createRecommendCategory())
|
||||||
e.printStackTrace()
|
errorMessage = "网络请求失败: ${e.message}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadAgentsByCategory(categoryId: Int) {
|
/**
|
||||||
loadAgentData(categoryId)
|
* 创建推荐分类
|
||||||
|
*/
|
||||||
|
private fun createRecommendCategory(): CategoryTemplate {
|
||||||
|
return CategoryTemplate(
|
||||||
|
id = 0,
|
||||||
|
name = "推荐",
|
||||||
|
description = "推荐内容",
|
||||||
|
avatar = "",
|
||||||
|
parentId = null,
|
||||||
|
parent = null,
|
||||||
|
children = null,
|
||||||
|
sort = 0,
|
||||||
|
isActive = true,
|
||||||
|
promptCount = 0,
|
||||||
|
createdAt = "",
|
||||||
|
updatedAt = "",
|
||||||
|
translations = null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadAllAgents() {
|
|
||||||
loadAgentData()
|
/**
|
||||||
|
* 加载更多网格Agent数据
|
||||||
|
*/
|
||||||
|
fun loadMoreGridAgentData() {
|
||||||
|
if (!gridHasMoreData || isLoading) return
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
isLoading = true
|
||||||
|
try {
|
||||||
|
val nextPage = gridCurrentPage + 1
|
||||||
|
val selectedCategory =
|
||||||
|
if (selectedCategoryIndex < categories.size) categories[selectedCategoryIndex] else null
|
||||||
|
|
||||||
|
val response = if (selectedCategoryIndex == 0 || selectedCategory == null) {
|
||||||
|
apiClient.getAgent(
|
||||||
|
page = nextPage,
|
||||||
|
pageSize = 20,
|
||||||
|
withWorkflow = "1",
|
||||||
|
random = "true"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
apiClient.getAgent(
|
||||||
|
page = nextPage,
|
||||||
|
pageSize = 20,
|
||||||
|
withWorkflow = "1",
|
||||||
|
categoryName = selectedCategory.name,
|
||||||
|
random = "true"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
val agents = response.body()?.data?.list ?: emptyList()
|
||||||
|
val newAgentItems = agents.map { agent ->
|
||||||
|
AgentItem.fromAgent(agent)
|
||||||
|
}
|
||||||
|
gridAgentItems = gridAgentItems + newAgentItems
|
||||||
|
gridCurrentPage = nextPage
|
||||||
|
gridHasMoreData = agents.size >= 20
|
||||||
|
} else {
|
||||||
|
errorMessage = "加载更多网格Agent数据失败: ${response.code()}"
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
errorMessage = "网络请求失败: ${e.message}"
|
||||||
|
} finally {
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载更多Agent数据
|
* 选择分类并加载对应的 Agent 数据
|
||||||
*/
|
*/
|
||||||
fun loadMoreAgents() {
|
fun selectCategory(categoryIndex: Int) {
|
||||||
// 检查是否正在加载或没有更多数据
|
if (categoryIndex != selectedCategoryIndex && categoryIndex >= 0 && categoryIndex < categories.size) {
|
||||||
if (isLoadingMore || !hasMoreData) {
|
selectedCategoryIndex = categoryIndex
|
||||||
return
|
// 同时加载顶部和网格的数据
|
||||||
|
loadTopAgentData(categoryIndex)
|
||||||
|
loadGridAgentData(categoryIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
val nextPage = currentPage + 1
|
|
||||||
loadAgentData(
|
|
||||||
categoryId = currentCategoryId,
|
|
||||||
page = nextPage,
|
|
||||||
isLoadMore = true
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createSingleChat(
|
fun createSingleChat(
|
||||||
openId: String,
|
openId: String,
|
||||||
) {
|
) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val response = ApiClient.api.createSingleChat(SingleChatRequestBody(agentOpenId = openId))
|
val response =
|
||||||
|
ApiClient.api.createSingleChat(SingleChatRequestBody(agentOpenId = openId))
|
||||||
|
Log.d("debug", response.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun goToChatAi(
|
fun goToChatAi(
|
||||||
openId: String,
|
openId: String,
|
||||||
navController: NavHostController
|
navController: NavHostController
|
||||||
) {
|
) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val profile = userService.getUserProfileByOpenId(openId)
|
val profile = userService.getUserProfileByOpenId(openId)
|
||||||
createGroup2ChatAi(profile.trtcUserId,"ai_group",navController,profile.id)
|
createGroup2ChatAi(profile.trtcUserId, "ai_group", navController, profile.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,15 +379,98 @@ object AgentViewModel: ViewModel() {
|
|||||||
* 刷新推荐Agent数据
|
* 刷新推荐Agent数据
|
||||||
*/
|
*/
|
||||||
fun refreshAgentData() {
|
fun refreshAgentData() {
|
||||||
loadAgentData()
|
loadTopAgentData()
|
||||||
|
loadGridAgentData()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查数据是否为空,如果为空则重新加载
|
* 检查数据是否为空,如果为空则重新加载
|
||||||
*/
|
*/
|
||||||
fun ensureDataLoaded() {
|
fun ensureDataLoaded() {
|
||||||
if (agentItems.isEmpty() && !isLoading) {
|
if (topAgentItems.isEmpty() && !isLoading) {
|
||||||
loadAgentData()
|
loadTopAgentData()
|
||||||
|
}
|
||||||
|
if (gridAgentItems.isEmpty() && !isLoading) {
|
||||||
|
loadGridAgentData()
|
||||||
|
}
|
||||||
|
// 同时确保分类数据已加载
|
||||||
|
if (categories.isEmpty() && !isLoading) {
|
||||||
|
loadCategories()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加入房间
|
||||||
|
*/
|
||||||
|
fun joinRoom(
|
||||||
|
id: Int,
|
||||||
|
name: String,
|
||||||
|
avatar: String,
|
||||||
|
context: Context,
|
||||||
|
navController: NavHostController,
|
||||||
|
onSuccess: () -> Unit,
|
||||||
|
onError: (String) -> Unit
|
||||||
|
) {
|
||||||
|
// 防止重复点击
|
||||||
|
if (isJoiningRoom) return
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
isJoiningRoom = true
|
||||||
|
val response = apiClient.joinRoom(JoinGroupChatRequestBody(roomId = id))
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
// 打开房间
|
||||||
|
val openRoomResponse = apiClient.createGroupChatAi(
|
||||||
|
roomId = id
|
||||||
|
)
|
||||||
|
|
||||||
|
if (openRoomResponse.isSuccessful){
|
||||||
|
val respData = openRoomResponse.body()
|
||||||
|
respData?.let {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
// 群聊直接使用群ID进行导航
|
||||||
|
navController.navigateToGroupChat(
|
||||||
|
id = respData.data.trtcRoomId,
|
||||||
|
name = name,
|
||||||
|
avatar = avatar
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
onError("加入房间失败")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onSuccess()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 处理错误响应
|
||||||
|
try {
|
||||||
|
val errorBody = response.errorBody()?.string()
|
||||||
|
if (errorBody != null) {
|
||||||
|
val gson = Gson()
|
||||||
|
val errorResponse = gson.fromJson(errorBody, ApiErrorResponse::class.java)
|
||||||
|
|
||||||
|
// 在主线程显示 Toast
|
||||||
|
Toast.makeText(context, errorResponse.error, Toast.LENGTH_LONG).show()
|
||||||
|
onError(errorResponse.error)
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, "加入房间失败", Toast.LENGTH_SHORT).show()
|
||||||
|
onError("加入房间失败")
|
||||||
|
}
|
||||||
|
} catch (parseException: Exception) {
|
||||||
|
// 如果解析错误响应失败,显示默认错误信息
|
||||||
|
Toast.makeText(context, "加入房间失败", Toast.LENGTH_SHORT).show()
|
||||||
|
onError("加入房间失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Toast.makeText(context, "网络请求失败:${e.message}", Toast.LENGTH_SHORT).show()
|
||||||
|
onError("网络请求失败:${e.message}")
|
||||||
|
} finally {
|
||||||
|
isJoiningRoom = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,33 +478,18 @@ object AgentViewModel: ViewModel() {
|
|||||||
* 重置ViewModel状态,用于登出或切换账号时清理数据
|
* 重置ViewModel状态,用于登出或切换账号时清理数据
|
||||||
*/
|
*/
|
||||||
fun ResetModel() {
|
fun ResetModel() {
|
||||||
agentItems = emptyList()
|
topAgentItems = emptyList()
|
||||||
|
gridAgentItems = emptyList()
|
||||||
|
categories = emptyList()
|
||||||
|
selectedCategoryIndex = 0
|
||||||
errorMessage = null
|
errorMessage = null
|
||||||
isRefreshing = false
|
isRefreshing = false
|
||||||
isLoading = false
|
isLoading = false
|
||||||
isLoadingMore = false
|
isJoiningRoom = false
|
||||||
currentPage = 1
|
topCurrentPage = 1
|
||||||
hasMoreData = true
|
topHasMoreData = true
|
||||||
currentCategoryId = null
|
gridCurrentPage = 1
|
||||||
|
gridHasMoreData = true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
data class CategoryItem(
|
|
||||||
val id: Int,
|
|
||||||
val name: String,
|
|
||||||
val description: String,
|
|
||||||
val avatar: String,
|
|
||||||
val promptCount: Int?
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
fun fromCategoryTemplate(template: CategoryTemplate): CategoryItem {
|
|
||||||
return CategoryItem(
|
|
||||||
id = template.id,
|
|
||||||
name = template.name,
|
|
||||||
description = template.description,
|
|
||||||
avatar = "${ApiClient.BASE_API_URL}${template.avatar}",
|
|
||||||
promptCount = template.promptCount
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@ package com.aiosman.ravenow.ui.index.tabs.message.tab
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.icu.util.Calendar
|
import android.icu.util.Calendar
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@@ -167,10 +168,12 @@ object GroupChatListViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun createGroupChat(
|
fun createGroupChat(
|
||||||
trtcGroupId: String,
|
trtcGroupId: String? = null,
|
||||||
|
roomId: Int? = null
|
||||||
) {
|
) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val response = ApiClient.api.createGroupChatAi(trtcGroupId = trtcGroupId)
|
val response = ApiClient.api.createGroupChatAi(trtcGroupId = trtcGroupId,roomId = roomId)
|
||||||
|
Log.d("debug",response.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -369,6 +369,7 @@ fun Explore() {
|
|||||||
trtcId = roomItem.trtcId.toString(),
|
trtcId = roomItem.trtcId.toString(),
|
||||||
name = roomItem.title,
|
name = roomItem.title,
|
||||||
avatar = roomItem.avatar,
|
avatar = roomItem.avatar,
|
||||||
|
context = context,
|
||||||
navController = navController,
|
navController = navController,
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
Toast.makeText(context, enterSuccessText, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, enterSuccessText, Toast.LENGTH_SHORT).show()
|
||||||
@@ -636,6 +637,7 @@ fun Explore() {
|
|||||||
trtcId = bannerItem.trtcId.toString(),
|
trtcId = bannerItem.trtcId.toString(),
|
||||||
name = bannerItem.title,
|
name = bannerItem.title,
|
||||||
avatar = bannerItem.avatar,
|
avatar = bannerItem.avatar,
|
||||||
|
context = context,
|
||||||
navController = navController,
|
navController = navController,
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
Toast.makeText(context, enterSuccessText, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, enterSuccessText, Toast.LENGTH_SHORT).show()
|
||||||
|
|||||||
@@ -17,32 +17,36 @@ import com.aiosman.ravenow.ui.index.tabs.message.MessageListViewModel.userServic
|
|||||||
import com.aiosman.ravenow.ui.index.tabs.message.tab.GroupChatListViewModel
|
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.index.tabs.message.tab.GroupChatListViewModel.createGroupChat
|
||||||
import com.aiosman.ravenow.ui.navigateToGroupChat
|
import com.aiosman.ravenow.ui.navigateToGroupChat
|
||||||
|
import com.aiosman.ravenow.data.api.ApiErrorResponse
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import android.content.Context
|
||||||
|
import android.widget.Toast
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class ExploreViewModel : ViewModel() {
|
class ExploreViewModel : ViewModel() {
|
||||||
|
|
||||||
private val apiClient: RaveNowAPI = ApiClient.api
|
private val apiClient: RaveNowAPI = ApiClient.api
|
||||||
|
|
||||||
var bannerItems by mutableStateOf<List<BannerItem>>(emptyList())
|
var bannerItems by mutableStateOf<List<BannerItem>>(emptyList())
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var agentItems by mutableStateOf<List<AgentItem>>(emptyList())
|
var agentItems by mutableStateOf<List<AgentItem>>(emptyList())
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var isLoading by mutableStateOf(false)
|
var isLoading by mutableStateOf(false)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var isRefreshing by mutableStateOf(false)
|
var isRefreshing by mutableStateOf(false)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var errorMessage by mutableStateOf<String?>(null)
|
var errorMessage by mutableStateOf<String?>(null)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
init {
|
init {
|
||||||
loadBannerData()
|
loadBannerData()
|
||||||
loadAgentData()
|
loadAgentData()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun refreshBannerData() {
|
fun refreshBannerData() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
isRefreshing = true
|
isRefreshing = true
|
||||||
@@ -64,13 +68,13 @@ class ExploreViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun refreshAgentData() {
|
fun refreshAgentData() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
isRefreshing = true
|
isRefreshing = true
|
||||||
errorMessage = null
|
errorMessage = null
|
||||||
try {
|
try {
|
||||||
val response = apiClient.getAgent(page = 1, pageSize = 20, withWorkflow = 1)
|
val response = apiClient.getAgent(page = 1, pageSize = 20, withWorkflow = "1")
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
val agents = response.body()?.data?.list ?: emptyList()
|
val agents = response.body()?.data?.list ?: emptyList()
|
||||||
agentItems = agents.map { agent ->
|
agentItems = agents.map { agent ->
|
||||||
@@ -86,7 +90,7 @@ class ExploreViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadBannerData() {
|
private fun loadBannerData() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
@@ -108,13 +112,13 @@ class ExploreViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadAgentData() {
|
private fun loadAgentData() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
errorMessage = null
|
errorMessage = null
|
||||||
try {
|
try {
|
||||||
val response = apiClient.getAgent(page = 1, pageSize = 20, withWorkflow = 1)
|
val response = apiClient.getAgent(page = 1, pageSize = 20, withWorkflow = "1")
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
val agents = response.body()?.data?.list ?: emptyList()
|
val agents = response.body()?.data?.list ?: emptyList()
|
||||||
agentItems = agents.map { agent ->
|
agentItems = agents.map { agent ->
|
||||||
@@ -130,21 +134,24 @@ class ExploreViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createSingleChat(
|
fun createSingleChat(
|
||||||
openId: String,
|
openId: String,
|
||||||
) {
|
) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val response = ApiClient.api.createSingleChat(SingleChatRequestBody(agentOpenId = openId))
|
val response =
|
||||||
|
ApiClient.api.createSingleChat(SingleChatRequestBody(agentOpenId = openId))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun goToChatAi(
|
fun goToChatAi(
|
||||||
openId: String,
|
openId: String,
|
||||||
navController: NavHostController
|
navController: NavHostController
|
||||||
) {
|
) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val profile = userService.getUserProfileByOpenId(openId)
|
val profile = userService.getUserProfileByOpenId(openId)
|
||||||
createGroup2ChatAi(profile.trtcUserId,"ai_group",navController,profile.id)
|
createGroup2ChatAi(profile.trtcUserId, "ai_group", navController, profile.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +159,7 @@ class ExploreViewModel : ViewModel() {
|
|||||||
trtcId: String,
|
trtcId: String,
|
||||||
name: String,
|
name: String,
|
||||||
avatar: String,
|
avatar: String,
|
||||||
|
context: Context,
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
onSuccess: () -> Unit,
|
onSuccess: () -> Unit,
|
||||||
onError: (String) -> Unit
|
onError: (String) -> Unit
|
||||||
@@ -160,24 +168,45 @@ class ExploreViewModel : ViewModel() {
|
|||||||
try {
|
try {
|
||||||
val response = apiClient.joinRoom(JoinGroupChatRequestBody(trtcId = trtcId))
|
val response = apiClient.joinRoom(JoinGroupChatRequestBody(trtcId = trtcId))
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
createGroupChat(trtcGroupId = trtcId)
|
createGroupChat(trtcGroupId = trtcId)
|
||||||
// 群聊直接使用群ID进行导航
|
// 群聊直接使用群ID进行导航
|
||||||
navController.navigateToGroupChat( id = trtcId,
|
navController.navigateToGroupChat(
|
||||||
name = name,
|
id = trtcId,
|
||||||
avatar = avatar)
|
name = name,
|
||||||
} catch (e: Exception) {
|
avatar = avatar
|
||||||
onError("加入房间失败")
|
)
|
||||||
e.printStackTrace()
|
} catch (e: Exception) {
|
||||||
}
|
onError("加入房间失败")
|
||||||
}
|
e.printStackTrace()
|
||||||
onSuccess()
|
}
|
||||||
|
}
|
||||||
|
onSuccess()
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
onError("加入房间失败")
|
// 处理错误响应
|
||||||
|
try {
|
||||||
|
val errorBody = response.errorBody()?.string()
|
||||||
|
if (errorBody != null) {
|
||||||
|
val gson = Gson()
|
||||||
|
val errorResponse = gson.fromJson(errorBody, ApiErrorResponse::class.java)
|
||||||
|
|
||||||
|
// 在主线程显示 Toast
|
||||||
|
Toast.makeText(context, errorResponse.error, Toast.LENGTH_LONG).show()
|
||||||
|
onError(errorResponse.error)
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, "加入房间失败", Toast.LENGTH_SHORT).show()
|
||||||
|
onError("加入房间失败")
|
||||||
|
}
|
||||||
|
} catch (parseException: Exception) {
|
||||||
|
// 如果解析错误响应失败,显示默认错误信息
|
||||||
|
Toast.makeText(context, "加入房间失败", Toast.LENGTH_SHORT).show()
|
||||||
|
onError("加入房间失败")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
Toast.makeText(context, "网络请求失败:${e.message}", Toast.LENGTH_SHORT).show()
|
||||||
onError("网络请求失败:${e.message}")
|
onError("网络请求失败:${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,6 +157,7 @@
|
|||||||
<string name="agent_chat_file">[ファイル]</string>
|
<string name="agent_chat_file">[ファイル]</string>
|
||||||
<string name="agent_chat_message">[メッセージ]</string>
|
<string name="agent_chat_message">[メッセージ]</string>
|
||||||
<string name="agent_chat_load_failed">読み込みに失敗しました</string>
|
<string name="agent_chat_load_failed">読み込みに失敗しました</string>
|
||||||
|
<string name="agent_chat_loading">読み込み中</string>
|
||||||
<string name="agent_chat_load_more_failed">さらに読み込むのに失敗しました</string>
|
<string name="agent_chat_load_more_failed">さらに読み込むのに失敗しました</string>
|
||||||
<string name="agent_chat_user_info_failed">ユーザー情報の取得に失敗しました: %s</string>
|
<string name="agent_chat_user_info_failed">ユーザー情報の取得に失敗しました: %s</string>
|
||||||
<string name="group_chat_empty">グループチャットがありません</string>
|
<string name="group_chat_empty">グループチャットがありません</string>
|
||||||
|
|||||||
@@ -161,14 +161,15 @@
|
|||||||
<string name="agent_chat_file">[文件]</string>
|
<string name="agent_chat_file">[文件]</string>
|
||||||
<string name="agent_chat_message">[消息]</string>
|
<string name="agent_chat_message">[消息]</string>
|
||||||
<string name="agent_chat_load_failed">加载失败</string>
|
<string name="agent_chat_load_failed">加载失败</string>
|
||||||
|
<string name="agent_chat_loading">加载中</string>
|
||||||
<string name="agent_chat_load_more_failed">加载更多失败</string>
|
<string name="agent_chat_load_more_failed">加载更多失败</string>
|
||||||
<string name="agent_chat_user_info_failed">获取用户信息失败: %s</string>
|
<string name="agent_chat_user_info_failed">获取用户信息失败: %s</string>
|
||||||
<string name="group_chat_empty">没有群聊,宇宙好安静</string>
|
<string name="group_chat_empty">没有群聊,宇宙好安静</string>
|
||||||
<string name="group_chat_empty_title">没有群聊消息的宇宙太安静了</string>
|
<string name="group_chat_empty_title">没有群聊消息的宇宙太安静了</string>
|
||||||
<string name="group_chat_empty_subtitle">在首页探索感兴趣的主题房间</string>
|
<string name="group_chat_empty_subtitle">在首页探索感兴趣的主题房间</string>
|
||||||
<string name="group_chat_empty_join">去首页探索感兴趣的高能对话</string>
|
<string name="group_chat_empty_join">去首页探索感兴趣的高能对话</string>
|
||||||
<string name="friend_chat_empty_title">和朋友,还没有对话哦~</string>
|
<string name="friend_chat_empty_title">你和朋友,还没说第一句话呢</string>
|
||||||
<string name="friend_chat_empty_subtitle">点击好友头像,即刻发起聊天</string>
|
<string name="friend_chat_empty_subtitle">一段崭新的友谊 等待被唤醒</string>
|
||||||
<string name="friend_chat_me_prefix">我: </string>
|
<string name="friend_chat_me_prefix">我: </string>
|
||||||
<string name="friend_chat_load_failed">加载失败</string>
|
<string name="friend_chat_load_failed">加载失败</string>
|
||||||
<string name="create_group_chat">创建群聊</string>
|
<string name="create_group_chat">创建群聊</string>
|
||||||
|
|||||||
@@ -147,6 +147,7 @@
|
|||||||
<string name="chat_friend">Friends</string>
|
<string name="chat_friend">Friends</string>
|
||||||
<string name="chat_all">All</string>
|
<string name="chat_all">All</string>
|
||||||
<string name="agent_chat_list_title">Agent Chat</string>
|
<string name="agent_chat_list_title">Agent Chat</string>
|
||||||
|
<string name="agent_chat_loading">Loading</string>
|
||||||
<string name="agent_chat_empty_title">No Agent Chat</string>
|
<string name="agent_chat_empty_title">No Agent Chat</string>
|
||||||
<string name="agent_chat_empty_subtitle">Start chatting with agents</string>
|
<string name="agent_chat_empty_subtitle">Start chatting with agents</string>
|
||||||
<string name="agent_chat_me_prefix">Me: </string>
|
<string name="agent_chat_me_prefix">Me: </string>
|
||||||
|
|||||||
Reference in New Issue
Block a user