Compare commits
15 Commits
new_create
...
update_age
| Author | SHA1 | Date | |
|---|---|---|---|
| d8df67bae5 | |||
| 742410223c | |||
| bd5aff7564 | |||
| b43c1585c4 | |||
| cb582393f1 | |||
| a200d00587 | |||
| 6d2133545f | |||
| 2aad126010 | |||
| b7b777d2d0 | |||
| e804c8be0c | |||
| 228a74695e | |||
| 41a51b85da | |||
| e74e8615a5 | |||
| 349d39daf2 | |||
| 8154a0ddc4 |
4
.idea/deploymentTargetSelector.xml
generated
@@ -4,10 +4,10 @@
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2025-09-09T09:51:06.656104400Z">
|
||||
<DropdownSelection timestamp="2025-09-17T06:25:35.585100400Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="Default" identifier="serial=192.168.0.227:45035;connection=094cb92e" />
|
||||
<DeviceId pluginId="Default" identifier="serial=192.168.0.216:5555;connection=698a7727" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
|
||||
@@ -81,7 +81,9 @@ data class CreateGroupChatRequestBody(
|
||||
|
||||
data class JoinGroupChatRequestBody(
|
||||
@SerializedName("trtcId")
|
||||
val trtcId: String,
|
||||
val trtcId: String? = null,
|
||||
@SerializedName("roomId")
|
||||
val roomId: Int? = null,
|
||||
)
|
||||
|
||||
data class LoginUserRequestBody(
|
||||
@@ -271,6 +273,170 @@ data class RemoveAccountRequestBody(
|
||||
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(
|
||||
@SerializedName("id")
|
||||
val id: Int,
|
||||
@SerializedName("name")
|
||||
val name: String,
|
||||
@SerializedName("description")
|
||||
val description: String,
|
||||
@SerializedName("avatar")
|
||||
val avatar: String,
|
||||
@SerializedName("parentId")
|
||||
val parentId: Int?,
|
||||
@SerializedName("parent")
|
||||
val parent: CategoryTemplate?,
|
||||
@SerializedName("children")
|
||||
val children: List<CategoryTemplate>?,
|
||||
@SerializedName("sort")
|
||||
val sort: Int,
|
||||
@SerializedName("isActive")
|
||||
val isActive: Boolean,
|
||||
@SerializedName("promptCount")
|
||||
val promptCount: Int?,
|
||||
@SerializedName("createdAt")
|
||||
val createdAt: String,
|
||||
@SerializedName("updatedAt")
|
||||
val updatedAt: String,
|
||||
@SerializedName("translations")
|
||||
val translations: Map<String, CategoryTemplateTranslation>?
|
||||
) {
|
||||
/**
|
||||
* 获取本地化名称,优先使用当前语言的翻译,如果没有则使用默认名称
|
||||
*/
|
||||
fun getLocalizedName(): String {
|
||||
// 这里可以根据需要添加国际化逻辑
|
||||
// 目前直接返回默认名称
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
interface RaveNowAPI {
|
||||
@GET("membership/config")
|
||||
@retrofit2.http.Headers("X-Requires-Auth: true")
|
||||
@@ -550,8 +716,26 @@ interface RaveNowAPI {
|
||||
suspend fun getAgent(
|
||||
@Query("page") page: Int = 1,
|
||||
@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("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("uncategorized") uncategorized: String? = null,
|
||||
): Response<DataContainer<ListContainer<Agent>>>
|
||||
|
||||
@GET("outside/my/prompts")
|
||||
@@ -580,7 +764,10 @@ interface RaveNowAPI {
|
||||
suspend fun agentMoment(@Body body: AgentMomentRequestBody): Response<DataContainer<String>>
|
||||
|
||||
@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")
|
||||
suspend fun createSingleChat(@Body body: SingleChatRequestBody): Response<DataContainer<Unit>>
|
||||
@@ -595,6 +782,7 @@ interface RaveNowAPI {
|
||||
suspend fun getRooms(@Query("page") page: Int = 1,
|
||||
@Query("pageSize") pageSize: Int = 20,
|
||||
@Query("isRecommended") isRecommended: Int = 1,
|
||||
@Query("random") random: Int? = null,
|
||||
): Response<ListContainer<Room>>
|
||||
|
||||
@GET("outside/rooms/detail")
|
||||
@@ -605,6 +793,29 @@ interface RaveNowAPI {
|
||||
suspend fun joinRoom(@Body body: JoinGroupChatRequestBody,
|
||||
): Response<DataContainer<Room>>
|
||||
|
||||
@GET("outside/categories")
|
||||
suspend fun getCategories(
|
||||
@Query("page") page: Int? = null,
|
||||
@Query("pageSize") pageSize: Int? = null,
|
||||
@Query("parentId") parentId: Int? = null,
|
||||
@Query("isActive") isActive: Boolean? = null,
|
||||
@Query("name") name: String? = null,
|
||||
@Query("withChildren") withChildren: Boolean? = null,
|
||||
@Query("withParent") withParent: Boolean? = null,
|
||||
@Query("withCount") withCount: Boolean? = null,
|
||||
@Query("hideEmpty") hideEmpty: Boolean? = null
|
||||
): Response<ListContainer<CategoryTemplate>>
|
||||
|
||||
@GET("outside/categories/tree")
|
||||
suspend fun getCategoryTree(
|
||||
@Query("withCount") withCount: Boolean? = null,
|
||||
@Query("hideEmpty") hideEmpty: Boolean? = null
|
||||
): Response<DataContainer<List<CategoryTemplate>>>
|
||||
|
||||
@GET("outside/categories/{id}")
|
||||
suspend fun getCategoryById(
|
||||
@Path("id") id: Int
|
||||
): Response<DataContainer<CategoryTemplate>>
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -29,10 +29,12 @@ import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
@@ -61,7 +63,17 @@ import com.aiosman.ravenow.ui.composables.form.FormTextInput2
|
||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import com.aiosman.ravenow.AppState
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
import androidx.compose.animation.core.RepeatMode
|
||||
import androidx.compose.animation.core.StartOffset
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.infiniteRepeatable
|
||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.offset
|
||||
/**
|
||||
* 添加智能体界面
|
||||
*/
|
||||
@@ -73,9 +85,10 @@ fun AddAgentScreen() {
|
||||
var agnetNameError by remember { mutableStateOf<String?>(null) }
|
||||
var agnetDescError by remember { mutableStateOf<String?>(null) }
|
||||
var errorMessage by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
var isProcessing by remember { mutableStateOf(false) }
|
||||
var showWaveAnimation by remember { mutableStateOf(false) }
|
||||
|
||||
|
||||
|
||||
fun onNameChange(value: String) {
|
||||
model.name = value.trim()
|
||||
agnetNameError = when {
|
||||
@@ -106,21 +119,22 @@ fun AddAgentScreen() {
|
||||
}
|
||||
navController.popBackStack()
|
||||
}
|
||||
|
||||
|
||||
// 页面进入时重置头像选择状态
|
||||
LaunchedEffect(Unit) {
|
||||
model.isSelectingAvatar = false
|
||||
}
|
||||
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(color = appColors.decentBackground),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
var showManualCreation by remember { mutableStateOf(false) }
|
||||
StatusBarSpacer()
|
||||
Box(
|
||||
modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp)
|
||||
modifier = Modifier.padding(horizontal = 14.dp, vertical = 16.dp)
|
||||
.background(color = appColors.decentBackground)
|
||||
) {
|
||||
// 自定义header,控制返回按钮行为
|
||||
@@ -148,80 +162,398 @@ fun AddAgentScreen() {
|
||||
stringResource(R.string.agent_add),
|
||||
fontWeight = FontWeight.W600,
|
||||
modifier = Modifier.weight(1f),
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = 17.sp,
|
||||
color = appColors.text
|
||||
)
|
||||
Spacer(modifier = Modifier.size(12.dp))
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.noRippleClickable {
|
||||
// 提交创建智能体的逻辑可以在这里实现
|
||||
},
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = "Add",
|
||||
tint = appColors.text
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(1.dp))
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(50.dp)
|
||||
.padding(horizontal = 20.dp),
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.mipmap.group_copy),
|
||||
contentDescription = "",
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(
|
||||
RoundedCornerShape(48.dp)
|
||||
),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.padding(start = 20.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "${AppState.profile?.nickName ?: "User"} 你好呀!今天想创造什么?",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.W600
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.padding(start = 20.dp)
|
||||
) {
|
||||
if (!showManualCreation) {
|
||||
Text(
|
||||
text = "只需要一句话,你的专属AI将在这里诞生。",
|
||||
fontSize = 14.sp,
|
||||
color = LocalAppTheme.current.text.copy(alpha = 0.6f),
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(44.dp))
|
||||
Box(
|
||||
modifier = Modifier.size(88.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
|
||||
if (!showManualCreation) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 20.dp)
|
||||
) {
|
||||
CustomAsyncImage(
|
||||
context,
|
||||
model.croppedBitmap,
|
||||
modifier = Modifier
|
||||
.size(88.dp)
|
||||
.clip(
|
||||
RoundedCornerShape(88.dp)
|
||||
),
|
||||
contentDescription = "",
|
||||
contentScale = ContentScale.Crop,
|
||||
placeholderRes = R.mipmap.rider_pro_agent_avatar
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
.clip(CircleShape)
|
||||
.background(appColors.main)
|
||||
.align(Alignment.BottomEnd)
|
||||
.noRippleClickable {
|
||||
// 设置正在选择头像的标志
|
||||
model.isSelectingAvatar = true
|
||||
navController.navigate(NavigationRoute.AgentImageCrop.route)
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
.fillMaxWidth()
|
||||
.height(95.dp)
|
||||
.shadow(
|
||||
elevation = 10.dp,
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
spotColor = Color(0x33F563FF),
|
||||
ambientColor = Color(0x99F563FF),
|
||||
clip = false
|
||||
)
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
listOf(
|
||||
Color(0xFF6246FF),
|
||||
Color(0xFF7C45ED)
|
||||
)
|
||||
),
|
||||
shape = RoundedCornerShape(10.dp)
|
||||
)
|
||||
.padding(0.5.dp)
|
||||
.background(
|
||||
color = appColors.inputBackground2,
|
||||
shape = RoundedCornerShape(10.dp)
|
||||
)
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Add,
|
||||
contentDescription = "Add",
|
||||
tint = Color.White,
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.noRippleClickable {
|
||||
model.viewModelScope.launch {
|
||||
focusRequester.requestFocus()
|
||||
keyboardController?.show()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
FormTextInput2(
|
||||
value = model.desc,
|
||||
hint = "一个会写诗的AI,一个懂你笑点的AI...",
|
||||
background = appColors.inputBackground2,
|
||||
focusRequester = focusRequester,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(95.dp)
|
||||
) { value ->
|
||||
onDescChange(value)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(end = 12.dp, bottom = 12.dp)
|
||||
.noRippleClickable {
|
||||
if (!isProcessing && model.desc.isNotEmpty()) {
|
||||
isProcessing = true
|
||||
model.viewModelScope.launch {
|
||||
try {
|
||||
//AI美化功能待实现
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
isProcessing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.mipmap.icons_info_magic),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp),
|
||||
tint = Color.Unspecified
|
||||
)
|
||||
Spacer(modifier = Modifier.width(5.dp))
|
||||
Text(
|
||||
text = "AI美化",
|
||||
color = Color(0xFF6246FF),
|
||||
fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
if (showWaveAnimation) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.Start)
|
||||
.padding(start = 20.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.size(18.dp)
|
||||
) {
|
||||
val infiniteTransition = rememberInfiniteTransition()
|
||||
val dot1Translation by infiniteTransition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = -12f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(
|
||||
durationMillis = 1000,
|
||||
easing = FastOutSlowInEasing
|
||||
),
|
||||
repeatMode = RepeatMode.Reverse,
|
||||
initialStartOffset = StartOffset(0)
|
||||
)
|
||||
)
|
||||
|
||||
val dot2Translation by infiniteTransition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = -12f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(
|
||||
durationMillis = 1000,
|
||||
easing = FastOutSlowInEasing
|
||||
),
|
||||
repeatMode = RepeatMode.Reverse,
|
||||
initialStartOffset = StartOffset(333)
|
||||
)
|
||||
)
|
||||
|
||||
val dot3Translation by infiniteTransition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = -12f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(
|
||||
durationMillis = 1000,
|
||||
easing = FastOutSlowInEasing
|
||||
),
|
||||
repeatMode = RepeatMode.Reverse,
|
||||
initialStartOffset = StartOffset(666)
|
||||
)
|
||||
)
|
||||
|
||||
// 三个彩色圆点
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(6.dp)
|
||||
.align(Alignment.BottomStart)
|
||||
.offset(y = dot1Translation.dp)
|
||||
.background(Color(0xFFFFD400), CircleShape)
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(6.dp)
|
||||
.align(Alignment.BottomCenter)
|
||||
.offset(y = dot2Translation.dp)
|
||||
.background(Color(0xFF2F80FF), CircleShape)
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(6.dp)
|
||||
.align(Alignment.BottomEnd)
|
||||
.offset(y = dot3Translation.dp)
|
||||
.background(Color(0xFF27C84D), CircleShape)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
Text(
|
||||
text = "正在为你构思",
|
||||
color = Color.Black.copy(alpha = 0.6f),
|
||||
fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
|
||||
} else {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.Start)
|
||||
.padding(start = 20.dp)
|
||||
.width(136.dp)
|
||||
.height(40.dp)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = Color(0x33858B98),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
)
|
||||
.background(
|
||||
color = appColors.background,
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
)
|
||||
.noRippleClickable {
|
||||
showManualCreation = true
|
||||
}
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 18.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.mipmap.icons_infor_edit),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "手动创造Ai",
|
||||
color = Color.Black,
|
||||
fontWeight = FontWeight.W600,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(58.dp))
|
||||
}else {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.align(Alignment.Start)
|
||||
) {
|
||||
// 添加新的一句话创造AI按钮
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.Start)
|
||||
.width(140.dp)
|
||||
.height(40.dp)
|
||||
.shadow(
|
||||
elevation = 10.dp,
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
spotColor = Color(0x33F563FF),
|
||||
ambientColor = Color(0x99F563FF),
|
||||
clip = false
|
||||
)
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
listOf(
|
||||
Color(0xFF6246FF),
|
||||
Color(0xFF7C45ED)
|
||||
)
|
||||
),
|
||||
shape = RoundedCornerShape(10.dp)
|
||||
)
|
||||
.padding(0.5.dp)
|
||||
.background(
|
||||
color = appColors.background,
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
)
|
||||
.noRippleClickable {
|
||||
showManualCreation = false
|
||||
}
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.mipmap.icons_info_magic),
|
||||
contentDescription = null,
|
||||
tint = Color.Black,
|
||||
modifier = Modifier.size(18.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "一句话创造AI",
|
||||
color = Color.Black,
|
||||
fontWeight = FontWeight.W600,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = "头像",
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.W600
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(72.dp)
|
||||
.clip(CircleShape)
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
colors = listOf(
|
||||
Color(0x777c45ed),
|
||||
Color(0x777c68ef),
|
||||
Color(0x557bd8f8)
|
||||
)
|
||||
)
|
||||
)
|
||||
.align(Alignment.Start)
|
||||
.noRippleClickable {
|
||||
// 设置正在选择头像的标志
|
||||
model.isSelectingAvatar = true
|
||||
navController.navigate(NavigationRoute.AgentImageCrop.route)
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.mipmap.icons_infor_edit),
|
||||
contentDescription = "Edit",
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(20.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(18.dp))
|
||||
// 原版两个输入框
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.agent_name),
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.W600
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
FormTextInput(
|
||||
value = model.name,
|
||||
label = stringResource(R.string.agent_name),
|
||||
hint = stringResource(R.string.agent_name_hint),
|
||||
hint = "给它取个名字,让它成为独一无二的你",
|
||||
background = appColors.inputBackground2,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) { value ->
|
||||
onNameChange(value)
|
||||
}
|
||||
// Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.agent_desc),
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.W600
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
FormTextInput2(
|
||||
value = model.desc,
|
||||
label = stringResource(R.string.agent_desc),
|
||||
hint = stringResource(R.string.agent_desc_hint),
|
||||
background = appColors.inputBackground2,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -229,38 +561,41 @@ fun AddAgentScreen() {
|
||||
onDescChange(value)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(58.dp))
|
||||
|
||||
// 错误信息显示
|
||||
errorMessage?.let { error ->
|
||||
Text(
|
||||
text = error,
|
||||
color = Color.Red,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
fontSize = 14.sp
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
|
||||
|
||||
// 错误信息显示
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
errorMessage?.let { error ->
|
||||
Text(
|
||||
text = error,
|
||||
color = Color.Red,
|
||||
modifier = Modifier
|
||||
.padding(bottom = 20.dp)
|
||||
.align(Alignment.Center),
|
||||
fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ActionButton(
|
||||
modifier = Modifier
|
||||
.width(345.dp)
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(bottom = 40.dp)
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
colors = listOf(
|
||||
Color(0xFFEE2A33),
|
||||
Color(0xFFD80264),
|
||||
Color(0xFF8468BC)
|
||||
Color(0x777c45ed),
|
||||
Color(0x777c68ef),
|
||||
Color(0x557bd8f8)
|
||||
)
|
||||
),
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
),
|
||||
),
|
||||
color = Color.White,
|
||||
backgroundColor = Color.Transparent,
|
||||
text = stringResource(R.string.agent_create),
|
||||
text = stringResource(R.string.create_confirm),
|
||||
isLoading = model.isUpdating,
|
||||
loadingText = stringResource(R.string.agent_createing),
|
||||
enabled = !model.isUpdating && validate()
|
||||
) {
|
||||
// 验证输入
|
||||
@@ -268,12 +603,19 @@ fun AddAgentScreen() {
|
||||
if (validationError != null) {
|
||||
// 显示验证错误
|
||||
errorMessage = validationError
|
||||
model.viewModelScope.launch {
|
||||
kotlinx.coroutines.delay(3000)
|
||||
errorMessage = null
|
||||
}
|
||||
return@ActionButton
|
||||
}
|
||||
|
||||
|
||||
// 清除之前的错误信息
|
||||
errorMessage = null
|
||||
|
||||
|
||||
// 显示波动动画
|
||||
showWaveAnimation = true
|
||||
|
||||
// 调用创建智能体API
|
||||
model.viewModelScope.launch {
|
||||
try {
|
||||
@@ -284,13 +626,16 @@ fun AddAgentScreen() {
|
||||
navController.popBackStack()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// 隐藏波动动画
|
||||
showWaveAnimation = false
|
||||
|
||||
// 显示错误信息
|
||||
errorMessage = "创建智能体失败: ${e.message}"
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,9 +66,9 @@ object AddAgentViewModel : ViewModel() {
|
||||
|
||||
fun validate(): String? {
|
||||
return when {
|
||||
name.isEmpty() -> "智能体名称不能为空"
|
||||
name.length < 2 -> "智能体名称长度不能少于2个字符"
|
||||
name.length > 20 -> "智能体名称长度不能超过20个字符"
|
||||
// name.isEmpty() -> "智能体名称不能为空"
|
||||
// name.length < 2 -> "智能体名称长度不能少于2个字符"
|
||||
// name.length > 20 -> "智能体名称长度不能超过20个字符"
|
||||
desc.isEmpty() -> "智能体描述不能为空"
|
||||
desc.length > 100 -> "智能体描述长度不能超过100个字符"
|
||||
else -> null
|
||||
|
||||
@@ -283,7 +283,7 @@ fun ChatAiScreen(userId: String) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(AppColors.decentBackground)
|
||||
.background(Color.White)
|
||||
.padding(paddingValues)
|
||||
) {
|
||||
LazyColumn(
|
||||
|
||||
@@ -69,72 +69,22 @@ fun EditCommentBottomModal(
|
||||
.background(AppColors.background)
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
if (replyComment == null) "Comment" else "Reply",
|
||||
fontWeight = FontWeight.W600,
|
||||
modifier = Modifier.weight(1f),
|
||||
fontSize = 20.sp,
|
||||
fontStyle = FontStyle.Italic,
|
||||
color = AppColors.text
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
if (replyComment != null) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.clip(CircleShape)
|
||||
) {
|
||||
CustomAsyncImage(
|
||||
context,
|
||||
replyComment.avatar,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentDescription = "Avatar",
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
replyComment.name,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 16.sp,
|
||||
color = AppColors.text
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
replyComment.comment,
|
||||
maxLines = 1,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 32.dp),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = AppColors.text
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.Top
|
||||
) {
|
||||
Box(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
.clip(RoundedCornerShape(20.dp))
|
||||
.background(Color.White)
|
||||
.border(1.dp, Color.Black, RoundedCornerShape(20.dp))
|
||||
.background(Color.Gray.copy(alpha = 0.1f))
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.Top
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
BasicTextField(
|
||||
value = text,
|
||||
@@ -149,31 +99,40 @@ fun EditCommentBottomModal(
|
||||
color = Color.Black,
|
||||
fontWeight = FontWeight.Normal
|
||||
),
|
||||
minLines = 1
|
||||
decorationBox = { innerTextField ->
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
innerTextField()
|
||||
if (text.isEmpty()) {
|
||||
Text(
|
||||
text = if (replyComment == null) "快来互动吧..." else "回复@${replyComment.name}",
|
||||
color = AppColors.text.copy(alpha = 0.3f), // 30%透明度
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Crossfade(
|
||||
targetState = text.isNotEmpty(), animationSpec = tween(500),
|
||||
label = ""
|
||||
) { isNotEmpty ->
|
||||
Icon(
|
||||
painter = painterResource(id = R.mipmap.rider_pro_moment_post),
|
||||
contentDescription = "Send",
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.align(Alignment.Top)
|
||||
.noRippleClickable {
|
||||
if (text.isNotEmpty()) {
|
||||
onSend(text)
|
||||
text = ""
|
||||
}
|
||||
},
|
||||
tint = if (isNotEmpty) Color.Unspecified else AppColors.nonActive
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Icon(
|
||||
painter = painterResource(id = R.mipmap.btn),
|
||||
contentDescription = "Send",
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.padding(top = 13.dp)
|
||||
.noRippleClickable {
|
||||
if (text.isNotEmpty()) {
|
||||
onSend(text)
|
||||
text = ""
|
||||
}
|
||||
},
|
||||
tint = Color.Unspecified
|
||||
)
|
||||
|
||||
}
|
||||
Spacer(modifier = Modifier.height(navBarHeight))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,15 +92,21 @@ fun MomentCard(
|
||||
showFollowButton = showFollowButton
|
||||
)
|
||||
}
|
||||
val lastClickTime = remember { mutableStateOf(0L) }
|
||||
val clickDelay = 500L
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.noRippleClickable {
|
||||
navController.navigateToPost(
|
||||
momentEntity.id,
|
||||
highlightCommentId = 0,
|
||||
initImagePagerIndex = imageIndex
|
||||
)
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (currentTime - lastClickTime.value > clickDelay) {
|
||||
lastClickTime.value = currentTime
|
||||
navController.navigateToPost(
|
||||
momentEntity.id,
|
||||
highlightCommentId = 0,
|
||||
initImagePagerIndex = imageIndex
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
MomentContentGroup(
|
||||
@@ -213,8 +219,7 @@ fun MomentPostLocation(location: String) {
|
||||
text = location,
|
||||
color = AppColors.secondaryText,
|
||||
fontSize = 12.sp,
|
||||
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -238,6 +243,8 @@ fun MomentTopRowGroup(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
) {
|
||||
val lastClickTime = remember { mutableStateOf(0L) }
|
||||
val clickDelay = 500L
|
||||
CustomAsyncImage(
|
||||
context,
|
||||
momentEntity.avatar,
|
||||
@@ -246,12 +253,16 @@ fun MomentTopRowGroup(
|
||||
.size(40.dp)
|
||||
.clip(RoundedCornerShape(40.dp))
|
||||
.noRippleClickable {
|
||||
navController.navigate(
|
||||
NavigationRoute.AccountProfile.route.replace(
|
||||
"{id}",
|
||||
momentEntity.authorId.toString()
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (currentTime - lastClickTime.value > clickDelay) {
|
||||
lastClickTime.value = currentTime
|
||||
navController.navigate(
|
||||
NavigationRoute.AccountProfile.route.replace(
|
||||
"{id}",
|
||||
momentEntity.authorId.toString()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
@@ -267,7 +278,19 @@ fun MomentTopRowGroup(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
MomentName(
|
||||
modifier = Modifier.weight(1f),
|
||||
modifier = Modifier.weight(1f)
|
||||
.noRippleClickable {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (currentTime - lastClickTime.value > clickDelay) {
|
||||
lastClickTime.value = currentTime
|
||||
navController.navigate(
|
||||
NavigationRoute.AccountProfile.route.replace(
|
||||
"{id}",
|
||||
momentEntity.authorId.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
name = momentEntity.nickname
|
||||
)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
@@ -416,6 +439,8 @@ fun MomentBottomOperateRowGroup(
|
||||
momentEntity: MomentEntity,
|
||||
imageIndex: Int = 0
|
||||
) {
|
||||
val lastClickTime = remember { mutableStateOf(0L) }
|
||||
val clickDelay = 500L
|
||||
var showCommentModal by remember { mutableStateOf(false) }
|
||||
if (showCommentModal) {
|
||||
ModalBottomSheet(
|
||||
@@ -451,90 +476,106 @@ fun MomentBottomOperateRowGroup(
|
||||
.height(56.dp)
|
||||
.padding(start = 16.dp, end = 0.dp)
|
||||
) {
|
||||
Row(
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
MomentOperateBtn(count = momentEntity.likeCount.toString()) {
|
||||
AnimatedLikeIcon(
|
||||
modifier = Modifier.size(24.dp),
|
||||
liked = momentEntity.liked
|
||||
) {
|
||||
onLikeClick()
|
||||
if (momentEntity.images.size > 1) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
momentEntity.images.forEachIndexed { index, _ ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(4.dp)
|
||||
.clip(CircleShape)
|
||||
.background(
|
||||
if (imageIndex == index) Color.Red else Color.Gray.copy(
|
||||
alpha = 0.5f
|
||||
)
|
||||
)
|
||||
.padding(1.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.noRippleClickable {
|
||||
onCommentClick()
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
MomentOperateBtn(
|
||||
icon = R.drawable.rider_pro_comment,
|
||||
count = momentEntity.commentCount.toString()
|
||||
)
|
||||
}
|
||||
|
||||
Box(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
,
|
||||
contentAlignment = Alignment.CenterEnd
|
||||
.weight(1f),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
MomentOperateBtn(count = momentEntity.favoriteCount.toString()) {
|
||||
AnimatedFavouriteIcon(
|
||||
modifier = Modifier.size(24.dp),
|
||||
isFavourite = momentEntity.isFavorite
|
||||
) {
|
||||
onFavoriteClick()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxHeight(),
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
MomentOperateBtn(count = momentEntity.favoriteCount.toString()) {
|
||||
AnimatedFavouriteIcon(
|
||||
modifier = Modifier.size(24.dp),
|
||||
isFavourite = momentEntity.isFavorite
|
||||
) {
|
||||
onFavoriteClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.wrapContentWidth()
|
||||
.fillMaxHeight()
|
||||
.noRippleClickable {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (currentTime - lastClickTime.value > clickDelay) {
|
||||
lastClickTime.value = currentTime
|
||||
onCommentClick()
|
||||
}
|
||||
},
|
||||
contentAlignment = Alignment.CenterEnd
|
||||
) {
|
||||
MomentOperateBtn(
|
||||
icon = R.drawable.rider_pro_comment,
|
||||
count = momentEntity.commentCount.toString()
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(24.dp))
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.wrapContentWidth()
|
||||
.fillMaxHeight(),
|
||||
contentAlignment = Alignment.CenterEnd
|
||||
) {
|
||||
MomentOperateBtn(count = momentEntity.likeCount.toString()) {
|
||||
AnimatedLikeIcon(
|
||||
modifier = Modifier.size(24.dp),
|
||||
liked = momentEntity.liked
|
||||
) {
|
||||
onLikeClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (momentEntity.images.size > 1) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
momentEntity.images.forEachIndexed { index, _ ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(4.dp)
|
||||
.clip(CircleShape)
|
||||
.background(
|
||||
if (imageIndex == index) Color.Red else Color.Gray.copy(
|
||||
alpha = 0.5f
|
||||
)
|
||||
)
|
||||
.padding(1.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentListLoading() {
|
||||
CircularProgressIndicator(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentWidth(Alignment.CenterHorizontally),
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentWidth(Alignment.CenterHorizontally),
|
||||
color = Color.Red
|
||||
)
|
||||
}
|
||||
@@ -15,9 +15,11 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -52,7 +54,7 @@ fun FormTextInput(
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.clip(RoundedCornerShape(25.dp))
|
||||
.background(background ?: AppColors.inputBackground)
|
||||
.let {
|
||||
if (error != null) {
|
||||
@@ -79,34 +81,49 @@ fun FormTextInput(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 16.dp)
|
||||
) {
|
||||
if (value.isEmpty()) {
|
||||
Text(
|
||||
text = hint ?: "",
|
||||
style = TextStyle(
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = AppColors.inputHint
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
BasicTextField(
|
||||
maxLines = 1,
|
||||
value = value,
|
||||
onValueChange = {
|
||||
onValueChange(it)
|
||||
},
|
||||
singleLine = true,
|
||||
textStyle = TextStyle(
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = AppColors.text
|
||||
),
|
||||
cursorBrush = SolidColor(AppColors.text),
|
||||
Icon(
|
||||
painter = painterResource(id = R.mipmap.icons_infor_edit),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
.align(Alignment.TopStart),
|
||||
tint = Color.Unspecified.copy(alpha = 0.4f)
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Spacer(modifier = Modifier.width(24.dp))
|
||||
Box(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
if (value.isEmpty()) {
|
||||
Text(
|
||||
text = hint ?: "",
|
||||
style = TextStyle(
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = AppColors.inputHint
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
BasicTextField(
|
||||
maxLines = 1,
|
||||
value = value,
|
||||
onValueChange = {
|
||||
onValueChange(it)
|
||||
},
|
||||
singleLine = true,
|
||||
textStyle = TextStyle(
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = AppColors.text
|
||||
),
|
||||
cursorBrush = SolidColor(AppColors.text),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,14 +15,18 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.res.painterResource
|
||||
@@ -44,6 +48,7 @@ fun FormTextInput2(
|
||||
error: String? = null,
|
||||
hint: String? = null,
|
||||
background: Color? = null,
|
||||
focusRequester: FocusRequester? = null,
|
||||
onValueChange: (String) -> Unit
|
||||
) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
@@ -52,7 +57,7 @@ fun FormTextInput2(
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.clip(RoundedCornerShape(25.dp))
|
||||
.background(background ?: AppColors.inputBackground)
|
||||
.let {
|
||||
if (error != null) {
|
||||
@@ -63,7 +68,7 @@ fun FormTextInput2(
|
||||
}
|
||||
.padding(17.dp),
|
||||
|
||||
) {
|
||||
) {
|
||||
label?.let {
|
||||
Text(
|
||||
text = it,
|
||||
@@ -79,34 +84,57 @@ fun FormTextInput2(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(top = 8.dp)
|
||||
) {
|
||||
if (value.isEmpty()) {
|
||||
Text(
|
||||
text = hint ?: "",
|
||||
style = TextStyle(
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = AppColors.inputHint
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
BasicTextField(
|
||||
maxLines = 5,
|
||||
value = value,
|
||||
onValueChange = {
|
||||
onValueChange(it)
|
||||
},
|
||||
textStyle = TextStyle(
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = AppColors.text,
|
||||
lineHeight = 20.sp
|
||||
),
|
||||
cursorBrush = SolidColor(AppColors.text),
|
||||
Icon(
|
||||
painter = painterResource(id = R.mipmap.icons_infor_edit),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
.align(Alignment.TopStart),
|
||||
tint = Color.Unspecified.copy(alpha = 0.4f)
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Spacer(modifier = Modifier.width(24.dp))
|
||||
Box(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
if (value.isEmpty()) {
|
||||
Text(
|
||||
text = hint ?: "",
|
||||
style = TextStyle(
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = AppColors.inputHint
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
BasicTextField(
|
||||
maxLines = 5,
|
||||
value = value,
|
||||
onValueChange = {
|
||||
onValueChange(it)
|
||||
},
|
||||
modifier = Modifier
|
||||
.let {
|
||||
if (focusRequester != null) {
|
||||
it.focusRequester(focusRequester)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
},
|
||||
textStyle = TextStyle(
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = AppColors.text,
|
||||
lineHeight = 20.sp
|
||||
),
|
||||
cursorBrush = SolidColor(AppColors.text),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -362,7 +362,7 @@ fun IndexScreen() {
|
||||
Text(
|
||||
text = it.label(),
|
||||
fontSize = 10.sp,
|
||||
color = if (isSelected) AppColors.brandColorsColor else AppColors.text,
|
||||
color = if (isSelected) Color.Blue else AppColors.text,
|
||||
fontWeight = if (isSelected) FontWeight.W600 else FontWeight.Normal
|
||||
)
|
||||
}
|
||||
|
||||
@@ -17,13 +17,13 @@ sealed class NavigationItem(
|
||||
|
||||
data object Home : NavigationItem("Home",
|
||||
icon = { R.drawable.rider_pro_nav_home },
|
||||
selectedIcon = { R.mipmap.rider_pro_nav_home_hl },
|
||||
selectedIcon = { R.mipmap.bars_x_buttons_home_s },
|
||||
label = { stringResource(R.string.main_home) }
|
||||
)
|
||||
|
||||
data object Ai : NavigationItem("Ai",
|
||||
icon = { R.mipmap.bars_x_buttons_discover_bold},
|
||||
selectedIcon = { R.mipmap.dynamic_hl },
|
||||
selectedIcon = { R.mipmap.bars_x_buttons_discover_fill },
|
||||
label = { stringResource(R.string.index_dynamic) }
|
||||
)
|
||||
// data object Ai : NavigationItem("Ai",
|
||||
@@ -40,13 +40,13 @@ sealed class NavigationItem(
|
||||
|
||||
data object Notification : NavigationItem("Notification",
|
||||
icon = { R.drawable.rider_pro_nav_notification },
|
||||
selectedIcon = { R.mipmap.rider_pro_nav_message_hl },
|
||||
selectedIcon = { R.mipmap.bars_x_buttons_chat_s },
|
||||
label = { stringResource(R.string.main_message) }
|
||||
)
|
||||
|
||||
data object Profile : NavigationItem("Profile",
|
||||
icon = { R.drawable.rider_pro_nav_profile },
|
||||
selectedIcon = { R.mipmap.rider_pro_nav_profile_hl },
|
||||
selectedIcon = { R.mipmap.bars_x_buttons_user_s },
|
||||
label = { stringResource(R.string.main_profile) }
|
||||
)
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
@@ -36,6 +37,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
@@ -47,6 +49,10 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
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.navigation.NavHostController
|
||||
import com.aiosman.ravenow.AppStore
|
||||
@@ -69,13 +75,11 @@ import com.aiosman.ravenow.utils.DebounceUtils
|
||||
import com.aiosman.ravenow.utils.ResourceCleanupManager
|
||||
import kotlinx.coroutines.launch
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.ui.zIndex
|
||||
|
||||
@OptIn( ExperimentalFoundationApi::class)
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun Agent() {
|
||||
val AppColors = LocalAppTheme.current
|
||||
@@ -95,10 +99,22 @@ fun Agent() {
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.ensureDataLoaded()
|
||||
}
|
||||
|
||||
|
||||
// 监听滚动状态,实现自动加载更多
|
||||
LaunchedEffect(scrollState) {
|
||||
snapshotFlow { scrollState.value }
|
||||
.collect { scrollValue ->
|
||||
val maxScroll = scrollState.maxValue
|
||||
if (scrollValue >= maxScroll - 100 && !viewModel.isLoading) {
|
||||
// 滚动到接近底部时加载更多网格数据
|
||||
viewModel.loadMoreGridAgentData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 防抖状态
|
||||
var lastClickTime by remember { mutableStateOf(0L) }
|
||||
|
||||
|
||||
// 页面退出时只清理必要的资源,不清理推荐Agent数据
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
@@ -107,100 +123,61 @@ fun Agent() {
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(scrollState)
|
||||
.padding(
|
||||
top = statusBarPaddingValues.calculateTopPadding(),
|
||||
bottom = navigationBarPaddings,
|
||||
start = 16.dp,
|
||||
end = 16.dp
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
Row(
|
||||
// 固定顶部搜索条
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.wrapContentHeight()
|
||||
.height(44.dp)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
.fillMaxWidth()
|
||||
.background(AppColors.background)
|
||||
.zIndex(999.0f)
|
||||
.height(44.dp + statusBarPaddingValues.calculateTopPadding())
|
||||
) {
|
||||
androidx.compose.material3.Text(
|
||||
text = "Rave AI",
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.W900,
|
||||
color = AppColors.text,
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight().padding(top = 32.dp, start = 16.dp, end = 16.dp),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = "Rave AI",
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.W900,
|
||||
color = AppColors.text,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
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)
|
||||
)
|
||||
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)
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(15.dp))
|
||||
// // 搜索框
|
||||
// Row(
|
||||
// modifier = Modifier
|
||||
// .height(36.dp)
|
||||
// .weight(1f)
|
||||
// .clip(shape = RoundedCornerShape(8.dp))
|
||||
// .background(AppColors.inputBackground)
|
||||
// .padding(horizontal = 8.dp, vertical = 0.dp)
|
||||
// .noRippleClickable {
|
||||
// // 搜索框点击事件
|
||||
// },
|
||||
// verticalAlignment = Alignment.CenterVertically
|
||||
// ) {
|
||||
// Icon(
|
||||
// painter = painterResource(id = R.drawable.rider_pro_nav_search),
|
||||
// contentDescription = null,
|
||||
// tint = AppColors.inputHint
|
||||
// )
|
||||
// Box {
|
||||
// Text(
|
||||
// text = stringResource(R.string.search),
|
||||
// modifier = Modifier.padding(start = 8.dp),
|
||||
// color = AppColors.inputHint,
|
||||
// fontSize = 17.sp
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// Spacer(modifier = Modifier.width(16.dp))
|
||||
// // 创建智能体
|
||||
// Icon(
|
||||
// modifier = Modifier
|
||||
// .size(36.dp)
|
||||
// .noRippleClickable {
|
||||
// if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
|
||||
// // 检查游客模式,如果是游客则跳转登录
|
||||
// if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.CREATE_AGENT)) {
|
||||
// navController.navigate(NavigationRoute.Login.route)
|
||||
// } else {
|
||||
// // 导航到添加智能体页面
|
||||
// navController.navigate(
|
||||
// NavigationRoute.AddAgent.route
|
||||
// )
|
||||
// }
|
||||
// }) {
|
||||
// lastClickTime = System.currentTimeMillis()
|
||||
// }
|
||||
// },
|
||||
// painter = painterResource(id = R.drawable.rider_pro_new_post_add_pic),
|
||||
// contentDescription = null,
|
||||
// tint = AppColors.text
|
||||
// )
|
||||
|
||||
// 可滚动的内容区域
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(scrollState)
|
||||
.padding(
|
||||
top = 44.dp + statusBarPaddingValues.calculateTopPadding() + 15.dp,
|
||||
bottom = navigationBarPaddings,
|
||||
start = 16.dp,
|
||||
end = 16.dp
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -208,166 +185,143 @@ fun Agent() {
|
||||
.padding(vertical = 8.dp)
|
||||
|
||||
) {
|
||||
// 使用 ViewModel 中的选中状态
|
||||
val selectedTabIndex = viewModel.selectedCategoryIndex
|
||||
|
||||
// // 标题
|
||||
// Row(
|
||||
// verticalAlignment = Alignment.CenterVertically,
|
||||
// modifier = Modifier.padding(bottom = 12.dp)
|
||||
// ) {
|
||||
// Image(
|
||||
// painter = painterResource(R.mipmap.rider_pro_agent2),
|
||||
// contentDescription = "agent",
|
||||
// modifier = Modifier.size(28.dp),
|
||||
//
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(4.dp))
|
||||
// androidx.compose.material3.Text(
|
||||
// text = stringResource(R.string.agent_recommend_agent),
|
||||
// fontSize = 16.sp,
|
||||
// fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
|
||||
// color = AppColors.text
|
||||
// )
|
||||
// }
|
||||
|
||||
var selectedTabIndex by remember { mutableStateOf(0) }
|
||||
|
||||
// 标签页
|
||||
LazyRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.padding(bottom = 16.dp),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
item {
|
||||
CustomTabItem(
|
||||
text = stringResource(R.string.agent_recommend),
|
||||
isSelected = selectedTabIndex == 0,
|
||||
onClick = {
|
||||
selectedTabIndex = 0
|
||||
// 动态标签页
|
||||
if (viewModel.categories.isNotEmpty()) {
|
||||
LazyRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.padding(bottom = 16.dp),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
viewModel.categories.forEachIndexed { index, category ->
|
||||
item {
|
||||
CustomTabItem(
|
||||
text = category.getLocalizedName(),
|
||||
isSelected = selectedTabIndex == index,
|
||||
onClick = {
|
||||
viewModel.selectCategory(index)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
TabSpacer()
|
||||
}
|
||||
|
||||
item {
|
||||
CustomTabItem(
|
||||
text = "scenes",
|
||||
isSelected = selectedTabIndex == 1,
|
||||
onClick = {
|
||||
selectedTabIndex = 1
|
||||
if (index < viewModel.categories.size - 1) {
|
||||
item {
|
||||
TabSpacer()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
TabSpacer()
|
||||
}
|
||||
|
||||
item {
|
||||
CustomTabItem(
|
||||
text = "voices",
|
||||
isSelected = selectedTabIndex == 2,
|
||||
onClick = {
|
||||
selectedTabIndex = 2
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
TabSpacer()
|
||||
}
|
||||
|
||||
item {
|
||||
CustomTabItem(
|
||||
text = "anime",
|
||||
isSelected = selectedTabIndex == 3,
|
||||
onClick = {
|
||||
selectedTabIndex = 3
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
TabSpacer()
|
||||
}
|
||||
|
||||
item {
|
||||
CustomTabItem(
|
||||
text = "assist",
|
||||
isSelected = selectedTabIndex == 4,
|
||||
onClick = {
|
||||
selectedTabIndex = 4
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when (selectedTabIndex) {
|
||||
0 -> {
|
||||
AgentViewPagerSection(agentItems = viewModel.agentItems.take(15), viewModel)
|
||||
}
|
||||
else -> {
|
||||
val shuffledAgents = viewModel.agentItems.shuffled().take(15)
|
||||
AgentViewPagerSection(agentItems = shuffledAgents, viewModel)
|
||||
}
|
||||
}
|
||||
// 显示当前选中分类的 Agent 数据
|
||||
AgentViewPagerSection(agentItems = viewModel.topAgentItems, viewModel)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight(),
|
||||
// center the tabs
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.Bottom
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.mipmap.rider_pro_agent2),
|
||||
contentDescription = "agent",
|
||||
modifier = Modifier.size(28.dp),
|
||||
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
androidx.compose.material3.Text(
|
||||
text = stringResource(R.string.agent_find),
|
||||
fontSize = 16.sp,
|
||||
fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
|
||||
color = AppColors.text
|
||||
// 推荐聊天房间
|
||||
ChatRoomsSection(
|
||||
chatRooms = viewModel.chatRooms,
|
||||
navController = LocalNavController.current
|
||||
)
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
}
|
||||
Spacer(modifier = Modifier.height(50.dp))
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
) {
|
||||
val agentItems = viewModel.agentItems.take(15)
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(2),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(50.dp)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight(),
|
||||
// center the tabs
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.Bottom
|
||||
) {
|
||||
items(agentItems) { agentItem ->
|
||||
AgentCardSquare(
|
||||
agentItem = agentItem,
|
||||
viewModel = viewModel,
|
||||
navController = LocalNavController.current
|
||||
Image(
|
||||
painter = painterResource(R.mipmap.rider_pro_agent2),
|
||||
contentDescription = "agent",
|
||||
modifier = Modifier.size(28.dp),
|
||||
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
androidx.compose.material3.Text(
|
||||
text = stringResource(R.string.agent_find),
|
||||
fontSize = 16.sp,
|
||||
fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
|
||||
color = AppColors.text
|
||||
)
|
||||
|
||||
}
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
// Agent两列网格布局
|
||||
AgentGridLayout(
|
||||
agentItems = viewModel.gridAgentItems,
|
||||
viewModel = viewModel,
|
||||
navController = LocalNavController.current
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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")
|
||||
@Composable
|
||||
fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navController: NavHostController) {
|
||||
fun AgentCardSquare(
|
||||
agentItem: AgentItem,
|
||||
viewModel: AgentViewModel,
|
||||
navController: NavHostController
|
||||
) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
val cardHeight = 180.dp
|
||||
val avatarSize = cardHeight / 3 // 头像大小为方块高度的三分之一
|
||||
@@ -379,7 +333,7 @@ fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navControll
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(cardHeight)
|
||||
.background(Color(0xFFE0E0E0), RoundedCornerShape(12.dp)) // 灰色背景
|
||||
.background(AppColors.secondaryBackground, RoundedCornerShape(12.dp)) // 主题背景
|
||||
.clickable {
|
||||
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
|
||||
viewModel.goToProfile(agentItem.openId, navController)
|
||||
@@ -394,7 +348,7 @@ fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navControll
|
||||
modifier = Modifier
|
||||
.offset(y = -avatarSize / 2)
|
||||
.size(avatarSize)
|
||||
.background(Color.White, RoundedCornerShape(avatarSize / 2))
|
||||
.background(AppColors.background, RoundedCornerShape(avatarSize / 2))
|
||||
.clip(RoundedCornerShape(avatarSize / 2)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
@@ -421,7 +375,7 @@ fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navControll
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = avatarSize / 2 + 8.dp, start = 8.dp, end = 8.dp, bottom = 8.dp),
|
||||
.padding(top = avatarSize / 2 + 8.dp, start = 8.dp, end = 8.dp, bottom = 48.dp), // 为底部聊天按钮留出空间
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
androidx.compose.material3.Text(
|
||||
@@ -443,49 +397,50 @@ fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navControll
|
||||
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f, fill = false)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
// 聊天按钮,位于底部居中
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(80.dp)
|
||||
.height(32.dp)
|
||||
.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,
|
||||
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
|
||||
// 聊天按钮,固定在底部居中,距离底部有一定边距
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(bottom = 12.dp) // 距离底部的边距
|
||||
.width(80.dp)
|
||||
.height(32.dp)
|
||||
.background(
|
||||
color = AppColors.inputBackground,
|
||||
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,
|
||||
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun AgentViewPagerSection(agentItems: List<AgentItem>,viewModel: AgentViewModel) {
|
||||
fun AgentViewPagerSection(agentItems: List<AgentItem>, viewModel: AgentViewModel) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
|
||||
// 每页显示5个agent
|
||||
@@ -558,7 +513,13 @@ fun AgentViewPagerSection(agentItems: List<AgentItem>,viewModel: AgentViewModel)
|
||||
}
|
||||
|
||||
@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(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
@@ -566,7 +527,11 @@ fun AgentPage(viewModel: AgentViewModel,agentItems: List<AgentItem>, page: Int,
|
||||
) {
|
||||
// 显示3个agent
|
||||
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) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
@@ -576,9 +541,9 @@ fun AgentPage(viewModel: AgentViewModel,agentItems: List<AgentItem>, page: Int,
|
||||
|
||||
@SuppressLint("SuspiciousIndentation")
|
||||
@Composable
|
||||
fun AgentCard2(viewModel: AgentViewModel,agentItem: AgentItem,navController: NavHostController) {
|
||||
fun AgentCard2(viewModel: AgentViewModel, agentItem: AgentItem, navController: NavHostController) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
|
||||
|
||||
// 防抖状态
|
||||
var lastClickTime by remember { mutableStateOf(0L) }
|
||||
|
||||
@@ -592,7 +557,7 @@ fun AgentCard2(viewModel: AgentViewModel,agentItem: AgentItem,navController: Nav
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.background(Color(0xFFF5F5F5), RoundedCornerShape(24.dp))
|
||||
.background(AppColors.secondaryBackground, RoundedCornerShape(24.dp))
|
||||
.clickable {
|
||||
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
|
||||
viewModel.goToProfile(agentItem.openId, navController)
|
||||
@@ -656,7 +621,7 @@ fun AgentCard2(viewModel: AgentViewModel,agentItem: AgentItem,navController: Nav
|
||||
modifier = Modifier
|
||||
.size(width = 60.dp, height = 32.dp)
|
||||
.background(
|
||||
color = Color(0X147c7480),
|
||||
color = AppColors.inputBackground,
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
)
|
||||
.clickable {
|
||||
@@ -686,3 +651,186 @@ 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_agent2),
|
||||
contentDescription = "chat room",
|
||||
modifier = Modifier.size(28.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
androidx.compose.material3.Text(
|
||||
text = "群聊",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
|
||||
color = AppColors.text
|
||||
)
|
||||
}
|
||||
|
||||
// 3行宫格布局
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
// 将聊天房间按3个一组分组
|
||||
chatRooms.chunked(3).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)
|
||||
)
|
||||
}
|
||||
// 如果这一行不足3个,添加空白占位
|
||||
repeat(3 - rowRooms.size) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChatRoomCard(
|
||||
chatRoom: ChatRoom,
|
||||
navController: NavHostController,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
val cardSize = 100.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,55 +1,131 @@
|
||||
package com.aiosman.ravenow.ui.index.tabs.ai
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.getValue
|
||||
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.AppState
|
||||
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.CategoryTemplate
|
||||
import com.aiosman.ravenow.data.api.CreateGroupChatRequestBody
|
||||
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.NavigationRoute
|
||||
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.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
|
||||
|
||||
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
|
||||
|
||||
var agentItems by mutableStateOf<List<AgentItem>>(emptyList())
|
||||
// 顶部Agent列表数据(用于ViewPager)
|
||||
var topAgentItems by mutableStateOf<List<AgentItem>>(emptyList())
|
||||
private set
|
||||
|
||||
// 底部网格布局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
|
||||
|
||||
var errorMessage by mutableStateOf<String?>(null)
|
||||
private set
|
||||
|
||||
|
||||
var isRefreshing by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
var isLoading by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
var isJoiningRoom by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
private var topCurrentPage = 1
|
||||
private var topHasMoreData = true
|
||||
|
||||
private var gridCurrentPage = 1
|
||||
private var gridHasMoreData = true
|
||||
|
||||
init {
|
||||
loadAgentData()
|
||||
loadTopAgentData()
|
||||
loadGridAgentData()
|
||||
loadChatRooms()
|
||||
loadCategories()
|
||||
}
|
||||
|
||||
private fun loadAgentData() {
|
||||
/**
|
||||
* 加载顶部Agent列表数据(用于ViewPager)
|
||||
*/
|
||||
private fun loadTopAgentData(categoryIndex: Int = 0) {
|
||||
viewModelScope.launch {
|
||||
isLoading = true
|
||||
errorMessage = null
|
||||
topCurrentPage = 1
|
||||
topHasMoreData = true
|
||||
try {
|
||||
val response = apiClient.getAgent(page = 1, pageSize = 20, withWorkflow = 1)
|
||||
val selectedCategory =
|
||||
if (categoryIndex < categories.size) categories[categoryIndex] else null
|
||||
|
||||
val response = if (categoryIndex == 0 || selectedCategory == null) {
|
||||
// 推荐分类或无效分类,加载所有 Agent
|
||||
apiClient.getAgent(
|
||||
page = topCurrentPage,
|
||||
pageSize = 15,
|
||||
withWorkflow = "1",
|
||||
random = "1"
|
||||
)
|
||||
} else {
|
||||
// 特定分类,使用 categoryName 参数
|
||||
apiClient.getAgent(
|
||||
page = topCurrentPage,
|
||||
pageSize = 15,
|
||||
withWorkflow = "1",
|
||||
categoryName = selectedCategory.name,
|
||||
random = "1"
|
||||
)
|
||||
}
|
||||
|
||||
if (response.isSuccessful) {
|
||||
val agents = response.body()?.data?.list ?: emptyList()
|
||||
agentItems = agents.map { agent ->
|
||||
topAgentItems = agents.map { agent ->
|
||||
AgentItem.fromAgent(agent)
|
||||
}
|
||||
topHasMoreData = agents.size >= 15
|
||||
} else {
|
||||
errorMessage = "获取Agent数据失败: ${response.code()}"
|
||||
errorMessage = "获取顶部Agent数据失败: ${response.code()}"
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
errorMessage = "网络请求失败: ${e.message}"
|
||||
@@ -58,21 +134,227 @@ object AgentViewModel: ViewModel() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载底部网格布局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 {
|
||||
// 特定分类,使用 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) {
|
||||
// 如果网络请求失败,使用默认数据
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadCategories() {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val categoriesResponse = apiClient.getCategories(
|
||||
page = 1,
|
||||
pageSize = 100,
|
||||
)
|
||||
if (categoriesResponse.isSuccessful && categoriesResponse.body() != null) {
|
||||
// 添加一个默认的"推荐"分类在第一位
|
||||
val recommendCategory = createRecommendCategory()
|
||||
val categoriesList = categoriesResponse.body()?.list ?: emptyList()
|
||||
categories = listOf(recommendCategory) + categoriesList
|
||||
|
||||
// 分类加载完成后,重新加载当前选中分类的 Agent 数据
|
||||
if (topAgentItems.isEmpty()) {
|
||||
loadTopAgentData(selectedCategoryIndex)
|
||||
}
|
||||
if (gridAgentItems.isEmpty()) {
|
||||
loadGridAgentData(selectedCategoryIndex)
|
||||
}
|
||||
} else {
|
||||
// 如果请求失败,使用默认分类
|
||||
categories = listOf(createRecommendCategory())
|
||||
errorMessage = "获取分类失败: ${categoriesResponse.code()}"
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// 如果网络请求失败,使用默认分类
|
||||
categories = listOf(createRecommendCategory())
|
||||
errorMessage = "网络请求失败: ${e.message}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建推荐分类
|
||||
*/
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 加载更多网格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) {
|
||||
// 推荐分类或无效分类,加载所有 Agent
|
||||
apiClient.getAgent(
|
||||
page = nextPage,
|
||||
pageSize = 20,
|
||||
withWorkflow = "1",
|
||||
random = "true"
|
||||
)
|
||||
} else {
|
||||
// 特定分类,使用 categoryName 参数
|
||||
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 数据
|
||||
*/
|
||||
fun selectCategory(categoryIndex: Int) {
|
||||
if (categoryIndex != selectedCategoryIndex && categoryIndex >= 0 && categoryIndex < categories.size) {
|
||||
selectedCategoryIndex = categoryIndex
|
||||
// 同时加载顶部和网格的数据
|
||||
loadTopAgentData(categoryIndex)
|
||||
loadGridAgentData(categoryIndex)
|
||||
}
|
||||
}
|
||||
|
||||
fun createSingleChat(
|
||||
openId: String,
|
||||
) {
|
||||
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(
|
||||
openId: String,
|
||||
navController: NavHostController
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val profile = userService.getUserProfileByOpenId(openId)
|
||||
createGroup2ChatAi(profile.trtcUserId,"ai_group",navController,profile.id)
|
||||
createGroup2ChatAi(profile.trtcUserId, "ai_group", navController, profile.id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,15 +381,98 @@ object AgentViewModel: ViewModel() {
|
||||
* 刷新推荐Agent数据
|
||||
*/
|
||||
fun refreshAgentData() {
|
||||
loadAgentData()
|
||||
loadTopAgentData()
|
||||
loadGridAgentData()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查数据是否为空,如果为空则重新加载
|
||||
*/
|
||||
fun ensureDataLoaded() {
|
||||
if (agentItems.isEmpty() && !isLoading) {
|
||||
loadAgentData()
|
||||
if (topAgentItems.isEmpty() && !isLoading) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,10 +480,18 @@ object AgentViewModel: ViewModel() {
|
||||
* 重置ViewModel状态,用于登出或切换账号时清理数据
|
||||
*/
|
||||
fun ResetModel() {
|
||||
agentItems = emptyList()
|
||||
topAgentItems = emptyList()
|
||||
gridAgentItems = emptyList()
|
||||
categories = emptyList()
|
||||
selectedCategoryIndex = 0
|
||||
errorMessage = null
|
||||
isRefreshing = false
|
||||
isLoading = false
|
||||
isJoiningRoom = false
|
||||
topCurrentPage = 1
|
||||
topHasMoreData = true
|
||||
gridCurrentPage = 1
|
||||
gridHasMoreData = true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.aiosman.ravenow.ui.index.tabs.message.tab
|
||||
|
||||
import android.content.Context
|
||||
import android.icu.util.Calendar
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -167,10 +168,12 @@ object GroupChatListViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
fun createGroupChat(
|
||||
trtcGroupId: String,
|
||||
trtcGroupId: String? = null,
|
||||
roomId: Int? = null
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val response = ApiClient.api.createGroupChatAi(trtcGroupId = trtcGroupId)
|
||||
val response = ApiClient.api.createGroupChatAi(trtcGroupId = trtcGroupId,roomId = roomId)
|
||||
Log.d("debug",response.toString())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@ import com.aiosman.ravenow.ui.index.tabs.search.SearchViewModel
|
||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||
import kotlinx.coroutines.launch
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import com.aiosman.ravenow.ui.composables.TabItem
|
||||
@@ -120,6 +122,8 @@ fun MomentsList() {
|
||||
//
|
||||
// }
|
||||
// Spacer(modifier = Modifier.width(16.dp))
|
||||
val lastClickTime = remember { mutableStateOf(0L) }
|
||||
val clickDelay = 500L
|
||||
Text(
|
||||
text = stringResource(R.string.moment),
|
||||
fontSize = 20.sp,
|
||||
@@ -137,7 +141,11 @@ fun MomentsList() {
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.noRippleClickable {
|
||||
navController.navigate(NavigationRoute.Search.route)
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (currentTime - lastClickTime.value > clickDelay) {
|
||||
lastClickTime.value = currentTime
|
||||
navController.navigate(NavigationRoute.Search.route)
|
||||
}
|
||||
},
|
||||
colorFilter = ColorFilter.tint(AppColors.text)
|
||||
)
|
||||
|
||||
@@ -369,6 +369,7 @@ fun Explore() {
|
||||
trtcId = roomItem.trtcId.toString(),
|
||||
name = roomItem.title,
|
||||
avatar = roomItem.avatar,
|
||||
context = context,
|
||||
navController = navController,
|
||||
onSuccess = {
|
||||
Toast.makeText(context, enterSuccessText, Toast.LENGTH_SHORT).show()
|
||||
@@ -636,6 +637,7 @@ fun Explore() {
|
||||
trtcId = bannerItem.trtcId.toString(),
|
||||
name = bannerItem.title,
|
||||
avatar = bannerItem.avatar,
|
||||
context = context,
|
||||
navController = navController,
|
||||
onSuccess = {
|
||||
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.createGroupChat
|
||||
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
|
||||
|
||||
class ExploreViewModel : ViewModel() {
|
||||
|
||||
|
||||
private val apiClient: RaveNowAPI = ApiClient.api
|
||||
|
||||
|
||||
var bannerItems by mutableStateOf<List<BannerItem>>(emptyList())
|
||||
private set
|
||||
|
||||
|
||||
var agentItems by mutableStateOf<List<AgentItem>>(emptyList())
|
||||
private set
|
||||
|
||||
|
||||
var isLoading by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
|
||||
var isRefreshing by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
|
||||
var errorMessage by mutableStateOf<String?>(null)
|
||||
private set
|
||||
|
||||
|
||||
init {
|
||||
loadBannerData()
|
||||
loadAgentData()
|
||||
}
|
||||
|
||||
|
||||
fun refreshBannerData() {
|
||||
viewModelScope.launch {
|
||||
isRefreshing = true
|
||||
@@ -64,13 +68,13 @@ class ExploreViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun refreshAgentData() {
|
||||
viewModelScope.launch {
|
||||
isRefreshing = true
|
||||
errorMessage = null
|
||||
try {
|
||||
val response = apiClient.getAgent(page = 1, pageSize = 20, withWorkflow = 1)
|
||||
val response = apiClient.getAgent(page = 1, pageSize = 20, withWorkflow = "1")
|
||||
if (response.isSuccessful) {
|
||||
val agents = response.body()?.data?.list ?: emptyList()
|
||||
agentItems = agents.map { agent ->
|
||||
@@ -86,7 +90,7 @@ class ExploreViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun loadBannerData() {
|
||||
viewModelScope.launch {
|
||||
isLoading = true
|
||||
@@ -108,13 +112,13 @@ class ExploreViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun loadAgentData() {
|
||||
viewModelScope.launch {
|
||||
isLoading = true
|
||||
errorMessage = null
|
||||
try {
|
||||
val response = apiClient.getAgent(page = 1, pageSize = 20, withWorkflow = 1)
|
||||
val response = apiClient.getAgent(page = 1, pageSize = 20, withWorkflow = "1")
|
||||
if (response.isSuccessful) {
|
||||
val agents = response.body()?.data?.list ?: emptyList()
|
||||
agentItems = agents.map { agent ->
|
||||
@@ -130,21 +134,24 @@ class ExploreViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createSingleChat(
|
||||
openId: String,
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val response = ApiClient.api.createSingleChat(SingleChatRequestBody(agentOpenId = openId))
|
||||
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)
|
||||
createGroup2ChatAi(profile.trtcUserId, "ai_group", navController, profile.id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +159,7 @@ class ExploreViewModel : ViewModel() {
|
||||
trtcId: String,
|
||||
name: String,
|
||||
avatar: String,
|
||||
context: Context,
|
||||
navController: NavHostController,
|
||||
onSuccess: () -> Unit,
|
||||
onError: (String) -> Unit
|
||||
@@ -160,24 +168,45 @@ class ExploreViewModel : ViewModel() {
|
||||
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()
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
createGroupChat(trtcGroupId = trtcId)
|
||||
// 群聊直接使用群ID进行导航
|
||||
navController.navigateToGroupChat(
|
||||
id = trtcId,
|
||||
name = name,
|
||||
avatar = avatar
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
onError("加入房间失败")
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
onSuccess()
|
||||
|
||||
} 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) {
|
||||
Toast.makeText(context, "网络请求失败:${e.message}", Toast.LENGTH_SHORT).show()
|
||||
onError("网络请求失败:${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,10 +50,12 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.graphics.nativeCanvas
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -369,39 +371,6 @@ fun ProfileV3(
|
||||
)
|
||||
}
|
||||
}
|
||||
if (isSelf&&isMain) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(
|
||||
top = statusBarPaddingValues.calculateTopPadding(),
|
||||
start = 8.dp,
|
||||
end = 8.dp
|
||||
)
|
||||
.noRippleClickable {
|
||||
IndexViewModel.openDrawer = true
|
||||
}
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(
|
||||
AppColors.background.copy(
|
||||
alpha = 0.7f
|
||||
)
|
||||
)
|
||||
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.rider_pro_more_horizon),
|
||||
contentDescription = "",
|
||||
|
||||
tint = AppColors.text
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Box(
|
||||
@@ -460,7 +429,7 @@ fun ProfileV3(
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 添加用户智能体行(智能体用户不显示)
|
||||
if (!isAiAccount) {
|
||||
UserAgentsRow(
|
||||
@@ -509,84 +478,99 @@ fun ProfileV3(
|
||||
|
||||
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.graphicsLayer {
|
||||
alpha = 1 - state.toolbarState.progress
|
||||
}
|
||||
.background(AppColors.profileBackground)
|
||||
.onGloballyPositioned {
|
||||
miniToolbarHeight = with(density) {
|
||||
it.size.height.toDp().value.toInt()
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.graphicsLayer {
|
||||
alpha = 1 - state.toolbarState.progress
|
||||
}
|
||||
.background(AppColors.profileBackground)
|
||||
.onGloballyPositioned {
|
||||
miniToolbarHeight = with(density) {
|
||||
it.size.height.toDp().value.toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
StatusBarSpacer()
|
||||
Row(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = 16.dp,
|
||||
vertical = 8.dp,
|
||||
).noRippleClickable {
|
||||
|
||||
},
|
||||
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (!isMain) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_back_icon), // Replace with your image resource
|
||||
contentDescription = "Back",
|
||||
modifier = Modifier
|
||||
.noRippleClickable {
|
||||
navController.navigateUp()
|
||||
}
|
||||
.size(24.dp),
|
||||
colorFilter = ColorFilter.tint(AppColors.text)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
CustomAsyncImage(
|
||||
LocalContext.current,
|
||||
profile?.avatar,
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
.clip(CircleShape),
|
||||
contentDescription = "",
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
Text(
|
||||
text = profile?.nickName ?: "",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.W600,
|
||||
color = AppColors.text
|
||||
)
|
||||
StatusBarSpacer()
|
||||
Row(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = 16.dp,
|
||||
vertical = 8.dp,
|
||||
).noRippleClickable {
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (!isMain) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_back_icon), // Replace with your image resource
|
||||
contentDescription = "Back",
|
||||
modifier = Modifier
|
||||
.noRippleClickable {
|
||||
navController.navigateUp()
|
||||
}
|
||||
.size(24.dp),
|
||||
colorFilter = ColorFilter.tint(AppColors.text)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
CustomAsyncImage(
|
||||
LocalContext.current,
|
||||
profile?.avatar,
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
.clip(CircleShape),
|
||||
contentDescription = "",
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
Text(
|
||||
text = profile?.nickName ?: "",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.W600,
|
||||
color = AppColors.text
|
||||
)
|
||||
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
if (isSelf&&isMain) {
|
||||
Box(
|
||||
modifier = Modifier.noRippleClickable {
|
||||
IndexViewModel.openDrawer = true
|
||||
}
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.padding(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.rider_pro_more_horizon),
|
||||
contentDescription = "",
|
||||
tint = AppColors.text
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
if (isSelf&&isMain) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(
|
||||
top = 32.dp ,
|
||||
end = 16.dp
|
||||
)
|
||||
.noRippleClickable {
|
||||
IndexViewModel.openDrawer = true
|
||||
}
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.rider_pro_more_horizon),
|
||||
contentDescription = "",
|
||||
tint = AppColors.text
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
}
|
||||
) {
|
||||
|
||||
@@ -279,6 +279,7 @@ fun LoginPage() {
|
||||
contentDescription = "Rave Now",
|
||||
modifier = Modifier
|
||||
.size(52.dp)
|
||||
.clip(RoundedCornerShape(10.dp))
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
|
||||
@@ -917,7 +917,20 @@ fun Header(
|
||||
Text(
|
||||
text = nickname ?: "",
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.weight(1f),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.debouncedClickable(debounceTime = 1000L) {
|
||||
userId?.let {
|
||||
debouncedNavigation {
|
||||
navController.navigate(
|
||||
NavigationRoute.AccountProfile.route.replace(
|
||||
"{id}",
|
||||
userId.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
color = AppColors.text,
|
||||
fontSize = 17.sp
|
||||
)
|
||||
@@ -1196,7 +1209,7 @@ fun PostImageView(
|
||||
)
|
||||
}
|
||||
|
||||
// Navigation and Indicator container
|
||||
// 图片导航控件
|
||||
if (images.size > 1) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@@ -1347,76 +1360,56 @@ fun CommentItem(
|
||||
}
|
||||
) {}
|
||||
) {
|
||||
Row {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = commentEntity.name,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 11.sp,
|
||||
color = AppColors.text
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = commentEntity.date.timeAgo(context),
|
||||
fontSize = 11.sp,
|
||||
color = Color.Gray
|
||||
)
|
||||
}
|
||||
Row (modifier = Modifier.padding(top = 4.dp)){
|
||||
if (isChild) {
|
||||
val annotatedText = buildAnnotatedString {
|
||||
if (commentEntity.replyUserId != null) {
|
||||
pushStringAnnotation(
|
||||
tag = "replyUser",
|
||||
annotation = commentEntity.replyUserId.toString()
|
||||
)
|
||||
withStyle(
|
||||
style = SpanStyle(
|
||||
fontWeight = FontWeight.W600,
|
||||
color = Color(0xFF6F94AE)
|
||||
)
|
||||
) {
|
||||
append("@${commentEntity.replyUserNickname}")
|
||||
}
|
||||
pop()
|
||||
}
|
||||
append(" ${commentEntity.comment}")
|
||||
|
||||
}
|
||||
Box {
|
||||
CustomClickableText(
|
||||
text = annotatedText,
|
||||
onClick = { offset ->
|
||||
annotatedText.getStringAnnotations(
|
||||
tag = "replyUser",
|
||||
start = offset,
|
||||
end = offset
|
||||
).firstOrNull()?.let {
|
||||
debouncedNavigation {
|
||||
navController.navigate(
|
||||
NavigationRoute.AccountProfile.route.replace(
|
||||
"{id}",
|
||||
it.item
|
||||
)
|
||||
)
|
||||
}
|
||||
Column(
|
||||
horizontalAlignment = Alignment.End
|
||||
) {
|
||||
AnimatedLikeIcon(
|
||||
liked = commentEntity.liked,
|
||||
onClick = {
|
||||
// 检查游客模式,如果是游客则跳转登录
|
||||
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.LIKE_MOMENT)) {
|
||||
debouncedNavigation {
|
||||
navController.navigate(NavigationRoute.Login.route)
|
||||
}
|
||||
},
|
||||
style = TextStyle(fontSize = 14.sp, color = AppColors.text),
|
||||
onLongPress = {
|
||||
onLongClick(commentEntity)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
} else {
|
||||
} else {
|
||||
onLike(commentEntity)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
Text(
|
||||
text = commentEntity.comment,
|
||||
fontSize = 13.sp,
|
||||
maxLines = Int.MAX_VALUE,
|
||||
softWrap = true,
|
||||
lineHeight = 20.sp,
|
||||
text = commentEntity.likes.toString(),
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = AppColors.text,
|
||||
modifier = Modifier.combinedClickable(
|
||||
modifier = Modifier.padding(top = 4.dp,end = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Text(
|
||||
text = commentEntity.comment,
|
||||
fontSize = 13.sp,
|
||||
maxLines = Int.MAX_VALUE,
|
||||
softWrap = true,
|
||||
lineHeight = 20.sp,
|
||||
color = AppColors.text,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(end = 50.dp)
|
||||
.padding(top = 0.dp)
|
||||
.combinedClickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null,
|
||||
onLongClick = {
|
||||
@@ -1427,48 +1420,21 @@ fun CommentItem(
|
||||
) {
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
Row (modifier = Modifier.padding(top = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,){
|
||||
AnimatedLikeIcon(
|
||||
liked = commentEntity.liked,
|
||||
onClick = {
|
||||
// 检查游客模式,如果是游客则跳转登录
|
||||
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.LIKE_MOMENT)) {
|
||||
debouncedNavigation {
|
||||
navController.navigate(NavigationRoute.Login.route)
|
||||
}
|
||||
} else {
|
||||
onLike(commentEntity)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Text(
|
||||
text = commentEntity.likes.toString(),
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = AppColors.text
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.like),
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = AppColors.nonActiveText,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(27.dp))
|
||||
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.rider_pro_comment),
|
||||
contentDescription = "",
|
||||
modifier = Modifier.size(16.dp),
|
||||
tint = AppColors.nonActiveText
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
|
||||
Row (
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
){
|
||||
Text(
|
||||
text = commentEntity.date.timeAgo(context),
|
||||
fontSize = 12.sp,
|
||||
color = Color.Gray
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.reply),
|
||||
fontSize = 12.sp,
|
||||
@@ -1541,6 +1507,7 @@ fun CommentItem(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun PostBottomBar(
|
||||
onCreateCommentClick: () -> Unit = {},
|
||||
@@ -1607,6 +1574,24 @@ fun PostBottomBar(
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
AnimatedFavouriteIcon(
|
||||
isFavourite = momentEntity?.isFavorite == true,
|
||||
onClick = {
|
||||
// 检查游客模式,如果是游客则跳转登录
|
||||
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.LIKE_MOMENT)) {
|
||||
debouncedNavigation {
|
||||
navController.navigate(NavigationRoute.Login.route)
|
||||
}
|
||||
} else {
|
||||
onFavoriteClick()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Text(text = momentEntity?.favoriteCount.toString(), color = AppColors.text)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
AnimatedLikeIcon(
|
||||
liked = momentEntity?.liked == true,
|
||||
onClick = {
|
||||
@@ -1623,24 +1608,6 @@ fun PostBottomBar(
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Text(text = momentEntity?.likeCount.toString(), color = AppColors.text)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
AnimatedFavouriteIcon(
|
||||
isFavourite = momentEntity?.isFavorite == true,
|
||||
onClick = {
|
||||
// 检查游客模式,如果是游客则跳转登录
|
||||
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.LIKE_MOMENT)) {
|
||||
debouncedNavigation {
|
||||
navController.navigate(NavigationRoute.Login.route)
|
||||
}
|
||||
} else {
|
||||
onFavoriteClick()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Text(text = momentEntity?.favoriteCount.toString(), color = AppColors.text)
|
||||
|
||||
}
|
||||
BottomNavigationPlaceholder(
|
||||
color = AppColors.background
|
||||
|
||||
BIN
app/src/main/res/mipmap-hdpi/bars_x_buttons_chat_s.png
Normal file
|
After Width: | Height: | Size: 773 B |
BIN
app/src/main/res/mipmap-hdpi/bars_x_buttons_discover_fill.png
Normal file
|
After Width: | Height: | Size: 927 B |
BIN
app/src/main/res/mipmap-hdpi/bars_x_buttons_home_n_copy.png
Normal file
|
After Width: | Height: | Size: 535 B |
BIN
app/src/main/res/mipmap-hdpi/bars_x_buttons_home_n_copy_2.png
Normal file
|
After Width: | Height: | Size: 772 B |
BIN
app/src/main/res/mipmap-hdpi/bars_x_buttons_user_s.png
Normal file
|
After Width: | Height: | Size: 631 B |
BIN
app/src/main/res/mipmap-hdpi/btn.png
Normal file
|
After Width: | Height: | Size: 975 B |
BIN
app/src/main/res/mipmap-hdpi/group_copy.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
app/src/main/res/mipmap-hdpi/icons_info_magic.png
Normal file
|
After Width: | Height: | Size: 327 B |
BIN
app/src/main/res/mipmap-hdpi/icons_infor_edit.png
Normal file
|
After Width: | Height: | Size: 294 B |
BIN
app/src/main/res/mipmap-mdpi/bars_x_buttons_chat_s.png
Normal file
|
After Width: | Height: | Size: 581 B |
BIN
app/src/main/res/mipmap-mdpi/bars_x_buttons_discover_fill.png
Normal file
|
After Width: | Height: | Size: 634 B |
BIN
app/src/main/res/mipmap-mdpi/bars_x_buttons_home_n_copy.png
Normal file
|
After Width: | Height: | Size: 414 B |
BIN
app/src/main/res/mipmap-mdpi/bars_x_buttons_home_n_copy_2.png
Normal file
|
After Width: | Height: | Size: 602 B |
BIN
app/src/main/res/mipmap-mdpi/bars_x_buttons_user_s.png
Normal file
|
After Width: | Height: | Size: 421 B |
BIN
app/src/main/res/mipmap-mdpi/btn.png
Normal file
|
After Width: | Height: | Size: 660 B |
BIN
app/src/main/res/mipmap-mdpi/group_copy.png
Normal file
|
After Width: | Height: | Size: 768 B |
BIN
app/src/main/res/mipmap-mdpi/icons_info_magic.png
Normal file
|
After Width: | Height: | Size: 276 B |
BIN
app/src/main/res/mipmap-mdpi/icons_infor_edit.png
Normal file
|
After Width: | Height: | Size: 260 B |
BIN
app/src/main/res/mipmap-xhdpi/bars_x_buttons_chat_s.png
Normal file
|
After Width: | Height: | Size: 912 B |
BIN
app/src/main/res/mipmap-xhdpi/bars_x_buttons_discover_fill.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
app/src/main/res/mipmap-xhdpi/bars_x_buttons_home_n_copy.png
Normal file
|
After Width: | Height: | Size: 623 B |
BIN
app/src/main/res/mipmap-xhdpi/bars_x_buttons_home_n_copy_2.png
Normal file
|
After Width: | Height: | Size: 967 B |
BIN
app/src/main/res/mipmap-xhdpi/bars_x_buttons_user_s.png
Normal file
|
After Width: | Height: | Size: 726 B |
BIN
app/src/main/res/mipmap-xhdpi/btn.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/mipmap-xhdpi/group_copy.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/mipmap-xhdpi/icons_info_magic.png
Normal file
|
After Width: | Height: | Size: 374 B |
BIN
app/src/main/res/mipmap-xhdpi/icons_infor_edit.png
Normal file
|
After Width: | Height: | Size: 330 B |
BIN
app/src/main/res/mipmap-xxhdpi/bars_x_buttons_chat_s.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/bars_x_buttons_discover_fill.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/bars_x_buttons_home_n_copy.png
Normal file
|
After Width: | Height: | Size: 816 B |
BIN
app/src/main/res/mipmap-xxhdpi/bars_x_buttons_home_n_copy_2.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/bars_x_buttons_user_s.png
Normal file
|
After Width: | Height: | Size: 966 B |
BIN
app/src/main/res/mipmap-xxhdpi/btn.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/group_copy.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/icons_info_magic.png
Normal file
|
After Width: | Height: | Size: 470 B |
BIN
app/src/main/res/mipmap-xxhdpi/icons_infor_edit.png
Normal file
|
After Width: | Height: | Size: 372 B |
BIN
app/src/main/res/mipmap-xxxhdpi/bars_x_buttons_chat_s.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/bars_x_buttons_discover_fill.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/bars_x_buttons_home_n_copy.png
Normal file
|
After Width: | Height: | Size: 1022 B |
BIN
app/src/main/res/mipmap-xxxhdpi/bars_x_buttons_home_n_copy_2.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/bars_x_buttons_user_s.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/btn.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/group_copy.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/icons_info_magic.png
Normal file
|
After Width: | Height: | Size: 569 B |
BIN
app/src/main/res/mipmap-xxxhdpi/icons_infor_edit.png
Normal file
|
After Width: | Height: | Size: 443 B |
@@ -17,7 +17,7 @@
|
||||
<string name="following_upper">关注</string>
|
||||
<string name="unfollow_upper">取消关注</string>
|
||||
<string name="comment_count">%d条评论</string>
|
||||
<string name="post_comment_hint">说点什么</string>
|
||||
<string name="post_comment_hint">快来互动吧...</string>
|
||||
<string name="follow_upper">关注</string>
|
||||
<string name="login_upper">登录</string>
|
||||
<string name="lets_ride_upper">确认</string>
|
||||
@@ -133,12 +133,13 @@
|
||||
<string name="agent_hot">热门</string>
|
||||
<string name="agent_recommend">推荐</string>
|
||||
<string name="agent_other">其他</string>
|
||||
<string name="agent_add">创建智能体</string>
|
||||
<string name="agent_add">创建AI</string>
|
||||
<string name="agent_name">名称</string>
|
||||
<string name="agent_name_hint">请输入名称</string>
|
||||
<string name="agent_desc">设定描述</string>
|
||||
<string name="agent_desc_hint">示例: 一位经验丰富的销售员,擅长通过幽默风趣的语言和生动的案例,将复杂的产品转化为客户易于理解并感兴趣的话题</string>
|
||||
<string name="agent_create">创建智能体</string>
|
||||
<string name="create_confirm">好的,就它了</string>
|
||||
<string name="moment_content_hint">需要一些灵感来写文章吗?让人工智能来帮你!</string>
|
||||
<string name="moment_ai_co">AI文案优化</string>
|
||||
<string name="moment_ai_delete">删除</string>
|
||||
|
||||
@@ -178,6 +178,7 @@
|
||||
<string name="agent_recommend_agent">推荐给你的智能体</string>
|
||||
<string name="hot_chat_room">正在高能对话中</string>
|
||||
<string name="create_agent">创建智能体</string>
|
||||
<string name="create_confirm">confirm</string>
|
||||
<string name="publish_dynamic">发布动态</string>
|
||||
<string name="hot_agent">热门智能体</string>
|
||||
<string name="group_room_enter">进入</string>
|
||||
|
||||