15 Commits

Author SHA1 Message Date
1a24136c35 优化AI界面,添加分页加载功能,支持动态加载更多智能体数据;重构UI布局 2025-09-23 11:57:11 +08:00
742410223c Merge pull request #29 from Zhong202501/main
手动创建AI界面调整
2025-09-23 10:37:15 +08:00
bd5aff7564 手动创建AI界面调整 2025-09-22 17:57:39 +08:00
b43c1585c4 Merge pull request #28 from Zhong202501/main
创建AI界面UI兼容;动态页面调整
2025-09-22 10:48:57 +08:00
cb582393f1 手动创造AI界面;调整输入框点击区域;创建AI时的三点彩色动画 2025-09-19 18:45:10 +08:00
a200d00587 UI调整 2025-09-18 18:19:19 +08:00
6d2133545f 动态详情页面评论调整 2025-09-18 18:16:54 +08:00
2aad126010 创建AI界面UI兼容;动态页面调整 2025-09-17 18:41:15 +08:00
b7b777d2d0 Merge pull request #26 from Zhong202501/new
首页底部导航栏图标;创建AI界面
2025-09-17 11:03:17 +08:00
e804c8be0c Merge pull request #20 from Kevinlinpr/new-bottom-create-button
Feat: Add Create Bottom Sheet and icons
2025-09-17 10:44:34 +08:00
228a74695e Merge pull request #22 from Zhong202501/main
添加Category接口
2025-09-17 10:39:22 +08:00
41a51b85da 首页底部导航栏图标;创建AI界面 2025-09-16 18:18:36 +08:00
e74e8615a5 Agent卡片组件UI;Agent聊天界面输入框显示问题 2025-09-15 14:06:05 +08:00
349d39daf2 修复BUG:我的界面右上角图标会跟随背景图一起向上滑走 2025-09-12 18:24:49 +08:00
8154a0ddc4 Category接口;Agent卡片组件背景颜色 2025-09-11 18:14:54 +08:00
64 changed files with 1364 additions and 773 deletions

View File

@@ -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>

View File

@@ -271,6 +271,44 @@ data class RemoveAccountRequestBody(
val password: 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
)
data class CategoryListResponse(
@SerializedName("page")
val page: Int,
@SerializedName("pageSize")
val pageSize: Int,
@SerializedName("total")
val total: Int,
@SerializedName("list")
val list: List<CategoryTemplate>
)
interface RaveNowAPI {
@GET("membership/config")
@retrofit2.http.Headers("X-Requires-Auth: true")
@@ -552,6 +590,7 @@ interface RaveNowAPI {
@Query("pageSize") pageSize: Int = 20,
@Query("withWorkflow") withWorkflow: Int = 1,
@Query("authorId") authorId: Int? = null,
@Query("categoryIds") categoryIds: List<Int>? = null,
): Response<DataContainer<ListContainer<Agent>>>
@GET("outside/my/prompts")
@@ -605,7 +644,38 @@ 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<DataContainer<CategoryListResponse>>
@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>>
@GET("outside/prompts")
suspend fun getPromptsByCategory(
@Query("categoryIds") categoryIds: List<Int>? = null,
@Query("categoryName") categoryName: String? = null,
@Query("uncategorized") uncategorized: String? = null,
@Query("page") page: Int? = null,
@Query("pageSize") pageSize: Int? = null
): Response<ListContainer<Agent>>
}

View File

@@ -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()
}
}
}
}
}
}
}

View File

@@ -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

View File

@@ -283,7 +283,7 @@ fun ChatAiScreen(userId: String) {
Box(
modifier = Modifier
.fillMaxSize()
.background(AppColors.decentBackground)
.background(Color.White)
.padding(paddingValues)
) {
LazyColumn(

View File

@@ -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))
}
}

View File

@@ -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
)
}

View File

@@ -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),
)
}
}
}

View File

@@ -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),
)
}
}
}

View File

@@ -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
)
}

View File

@@ -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) }
)

View File

@@ -28,9 +28,14 @@ import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -69,13 +74,24 @@ 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.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
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.foundation.lazy.grid.items as gridItems
@OptIn( ExperimentalFoundationApi::class)
// 检测是否接近列表底部的扩展函数
fun LazyListState.isScrolledToEnd(buffer: Int = 3): Boolean {
val layoutInfo = this.layoutInfo
val totalItemsCount = layoutInfo.totalItemsCount
val lastVisibleItemIndex = layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0
return lastVisibleItemIndex >= (totalItemsCount - buffer)
}
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
@Composable
fun Agent() {
val AppColors = LocalAppTheme.current
@@ -89,16 +105,15 @@ fun Agent() {
var scope = rememberCoroutineScope()
val viewModel: AgentViewModel = viewModel()
val scrollState = rememberScrollState()
// 确保推荐Agent数据已加载
LaunchedEffect(Unit) {
viewModel.ensureDataLoaded()
}
// 防抖状态
var lastClickTime by remember { mutableStateOf(0L) }
// 页面退出时只清理必要的资源不清理推荐Agent数据
DisposableEffect(Unit) {
onDispose {
@@ -107,259 +122,270 @@ fun Agent() {
}
}
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState)
.padding(
top = statusBarPaddingValues.calculateTopPadding(),
bottom = navigationBarPaddings,
start = 16.dp,
end = 16.dp
),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Row(
modifier = Modifier
.wrapContentHeight()
.height(44.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
androidx.compose.material3.Text(
text = "Rave AI",
fontSize = 20.sp,
fontWeight = FontWeight.W900,
color = AppColors.text,
modifier = Modifier
.align(Alignment.CenterVertically)
)
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)
)
val agentItems = viewModel.agentItems
var selectedTabIndex by remember { mutableStateOf(0) }
// 无限滚动状态
val listState = rememberLazyListState()
// 创建一个可观察的滚动到底部状态
val isScrolledToEnd by remember {
derivedStateOf {
listState.isScrolledToEnd()
}
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
// )
}
// 检测滚动到底部并加载更多数据
LaunchedEffect(isScrolledToEnd) {
if (isScrolledToEnd && !viewModel.isLoadingMore && agentItems.isNotEmpty() && viewModel.hasMoreData) {
viewModel.loadMoreAgents()
}
}
Column(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
Scaffold(
topBar = {
TopAppBar(
title = {
androidx.compose.material3.Text(
text = "Rave AI",
fontSize = 20.sp,
fontWeight = FontWeight.W900,
color = AppColors.text
)
},
actions = {
Image(
painter = painterResource(id = R.drawable.rider_pro_nav_search),
contentDescription = "search",
modifier = Modifier
.size(24.dp)
.noRippleClickable {
navController.navigate(NavigationRoute.Search.route)
},
colorFilter = ColorFilter.tint(AppColors.text)
)
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = AppColors.background
)
)
},
containerColor = AppColors.background,
contentWindowInsets = WindowInsets(0, 0, 0, 0)
) { paddingValues ->
LazyColumn(
state = listState,
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.padding(
bottom = navigationBarPaddings,
start = 16.dp,
end = 16.dp
)
) {
) {
// // 标题
// Row(
// verticalAlignment = Alignment.CenterVertically,
// modifier = Modifier.padding(bottom = 12.dp)
// ) {
// Image(
// painter = painterResource(R.mipmap.rider_pro_agent2),
// contentDescription = "agent",
// modifier = Modifier.size(28.dp),
//
// )
// Spacer(modifier = Modifier.width(4.dp))
// androidx.compose.material3.Text(
// text = stringResource(R.string.agent_recommend_agent),
// fontSize = 16.sp,
// fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
// color = AppColors.text
// )
// }
var selectedTabIndex by remember { mutableStateOf(0) }
// 标签页
LazyRow(
// 类别标签页 - 吸顶
stickyHeader(key = "category_tabs") {
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(bottom = 16.dp),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
.background(AppColors.background)
.padding(top = 4.dp, bottom = 8.dp)
) {
item {
CustomTabItem(
text = stringResource(R.string.agent_recommend),
isSelected = selectedTabIndex == 0,
onClick = {
selectedTabIndex = 0
LazyRow(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(bottom = 8.dp),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
item {
CustomTabItem(
text = stringResource(R.string.agent_recommend),
isSelected = selectedTabIndex == 0,
onClick = {
selectedTabIndex = 0
viewModel.loadAllAgents()
}
)
}
item {
TabSpacer()
}
// 动态添加分类标签
viewModel.categories.take(4).forEachIndexed { index, category ->
item {
CustomTabItem(
text = category.name,
isSelected = selectedTabIndex == index + 1,
onClick = {
selectedTabIndex = index + 1
viewModel.loadAgentsByCategory(category.id)
}
)
}
)
}
item {
TabSpacer()
}
item {
CustomTabItem(
text = "scenes",
isSelected = selectedTabIndex == 1,
onClick = {
selectedTabIndex = 1
item {
TabSpacer()
}
)
}
}
item {
TabSpacer()
}
item {
CustomTabItem(
text = "scenes",
isSelected = selectedTabIndex == 1,
onClick = {
selectedTabIndex = 1
}
)
}
item {
CustomTabItem(
text = "voices",
isSelected = selectedTabIndex == 2,
onClick = {
selectedTabIndex = 2
}
)
}
item {
TabSpacer()
}
item {
TabSpacer()
}
item {
CustomTabItem(
text = "voices",
isSelected = selectedTabIndex == 6,
onClick = {
selectedTabIndex = 6
}
)
}
item {
CustomTabItem(
text = "anime",
isSelected = selectedTabIndex == 3,
onClick = {
selectedTabIndex = 3
}
)
}
item {
TabSpacer()
}
item {
TabSpacer()
}
item {
CustomTabItem(
text = "anime",
isSelected = selectedTabIndex == 7,
onClick = {
selectedTabIndex = 7
}
)
}
item {
CustomTabItem(
text = "assist",
isSelected = selectedTabIndex == 4,
onClick = {
selectedTabIndex = 4
}
)
item {
TabSpacer()
}
item {
CustomTabItem(
text = "assist",
isSelected = selectedTabIndex == 8,
onClick = {
selectedTabIndex = 8
}
)
}
}
}
when (selectedTabIndex) {
0 -> {
AgentViewPagerSection(agentItems = viewModel.agentItems.take(15), viewModel)
}
else -> {
val shuffledAgents = viewModel.agentItems.shuffled().take(15)
AgentViewPagerSection(agentItems = shuffledAgents, viewModel)
}
// 推荐内容区域
item {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
) {
when {
selectedTabIndex == 0 -> {
AgentViewPagerSection(agentItems = viewModel.agentItems.take(15), viewModel)
}
selectedTabIndex in 1..viewModel.categories.size -> {
AgentViewPagerSection(agentItems = viewModel.agentItems.take(15), viewModel)
}
else -> {
val shuffledAgents = viewModel.agentItems.shuffled().take(15)
AgentViewPagerSection(agentItems = shuffledAgents, 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
)
}
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)
) {
items(agentItems) { agentItem ->
AgentCardSquare(
agentItem = agentItem,
viewModel = viewModel,
navController = LocalNavController.current
// "发现更多" 标题 - 吸顶
stickyHeader(key = "discover_more") {
Row(
modifier = Modifier
.fillMaxWidth()
.background(AppColors.background)
.padding(top = 8.dp, bottom = 12.dp),
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
)
}
}
// Agent网格 - 使用行式布局
items(
items = agentItems.chunked(2),
key = { row -> row.firstOrNull()?.openId ?: "" }
) { rowItems ->
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
rowItems.forEach { agentItem ->
Box(
modifier = Modifier.weight(1f)
) {
AgentCardSquare(
agentItem = agentItem,
viewModel = viewModel,
navController = LocalNavController.current
)
}
}
// 如果这一行只有一个item添加一个空的占位符
if (rowItems.size == 1) {
Spacer(modifier = Modifier.weight(1f))
}
}
}
// 加载更多指示器
if (viewModel.isLoadingMore) {
item {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 24.dp),
horizontalArrangement = Arrangement.Center
) {
androidx.compose.material3.CircularProgressIndicator(
modifier = Modifier.size(24.dp),
color = AppColors.text,
strokeWidth = 2.dp
)
Spacer(modifier = Modifier.width(12.dp))
androidx.compose.material3.Text(
text = "加载中...",
color = AppColors.secondaryText,
fontSize = 14.sp
)
}
}
}
}
@@ -369,7 +395,7 @@ fun Agent() {
@Composable
fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navController: NavHostController) {
val AppColors = LocalAppTheme.current
val cardHeight = 180.dp
val cardHeight = 200.dp
val avatarSize = cardHeight / 3 // 头像大小为方块高度的三分之一
// 防抖状态
@@ -378,8 +404,9 @@ fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navControll
Box(
modifier = Modifier
.fillMaxWidth()
.padding(top = avatarSize / 2)
.height(cardHeight)
.background(Color(0xFFE0E0E0), RoundedCornerShape(12.dp)) // 灰色背景
.background(AppColors.nonActive, RoundedCornerShape(12.dp)) // 修改背景颜色
.clickable {
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
viewModel.goToProfile(agentItem.openId, navController)
@@ -426,7 +453,7 @@ fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navControll
) {
androidx.compose.material3.Text(
text = agentItem.title,
fontSize = 16.sp,
fontSize = 14.sp,
fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
color = AppColors.text,
maxLines = 1,
@@ -435,21 +462,26 @@ fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navControll
Spacer(modifier = Modifier.height(8.dp))
androidx.compose.material3.Text(
text = agentItem.desc,
fontSize = 14.sp,
color = AppColors.secondaryText,
maxLines = 2,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
modifier = Modifier.weight(1f, fill = false)
)
Box(
modifier = Modifier
.height(85.dp)
.fillMaxWidth()
) {
androidx.compose.material3.Text(
text = agentItem.desc,
fontSize = 12.sp,
color = AppColors.secondaryText,
maxLines = 5,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
)
}
Spacer(modifier = Modifier.height(8.dp))
// 聊天按钮,位于底部居中
Box(
modifier = Modifier
.width(80.dp)
.width(60.dp)
.height(32.dp)
.background(
color = Color(0X147c7480),

View File

@@ -6,7 +6,10 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController
import com.aiosman.ravenow.data.Agent
import com.aiosman.ravenow.data.ListContainer
import com.aiosman.ravenow.data.api.ApiClient
import com.aiosman.ravenow.data.api.CategoryTemplate
import com.aiosman.ravenow.data.api.RaveNowAPI
import com.aiosman.ravenow.data.api.SingleChatRequestBody
import com.aiosman.ravenow.ui.index.tabs.ai.tabs.mine.MineAgentViewModel.createGroup2ChatAi
@@ -22,6 +25,8 @@ object AgentViewModel: ViewModel() {
var agentItems by mutableStateOf<List<AgentItem>>(emptyList())
private set
var categories by mutableStateOf<List<CategoryItem>>(emptyList())
private set
var errorMessage by mutableStateOf<String?>(null)
private set
@@ -33,31 +38,140 @@ object AgentViewModel: ViewModel() {
var isLoading by mutableStateOf(false)
private set
// 分页相关状态
var isLoadingMore by mutableStateOf(false)
private set
var currentPage by mutableStateOf(1)
private set
var hasMoreData by mutableStateOf(true)
private set
private val pageSize = 20
private var currentCategoryId: Int? = null
init {
loadAgentData()
loadCategories()
}
private fun loadAgentData() {
private fun loadAgentData(categoryId: Int? = null, page: Int = 1, isLoadMore: Boolean = false) {
viewModelScope.launch {
isLoading = true
if (isLoadMore) {
isLoadingMore = true
} else {
isLoading = true
// 重置分页状态
currentPage = 1
hasMoreData = true
currentCategoryId = categoryId
}
errorMessage = null
try {
val response = apiClient.getAgent(page = 1, pageSize = 20, withWorkflow = 1)
val response = if (categoryId != null) {
// 根据分类ID获取智能体
apiClient.getAgent(
page = page,
pageSize = pageSize,
withWorkflow = 1,
categoryIds = listOf(categoryId)
)
} else {
// 获取所有智能体
apiClient.getAgent(page = page, pageSize = pageSize, withWorkflow = 1)
}
if (response.isSuccessful) {
val agents = response.body()?.data?.list ?: emptyList()
agentItems = agents.map { agent ->
val responseData = response.body()?.data
val agents = responseData?.list ?: emptyList<Agent>()
val newAgentItems = agents.map { agent ->
AgentItem.fromAgent(agent)
}
if (isLoadMore) {
// 加载更多:追加到现有列表
agentItems = agentItems + newAgentItems
currentPage = page
} else {
// 首次加载或刷新:替换整个列表
agentItems = newAgentItems
currentPage = 1
}
// 检查是否还有更多数据
hasMoreData = agents.size >= pageSize
} else {
errorMessage = "获取Agent数据失败: ${response.code()}"
}
} catch (e: Exception) {
errorMessage = "网络请求失败: ${e.message}"
} finally {
isLoading = false
if (isLoadMore) {
isLoadingMore = false
} else {
isLoading = false
}
}
}
}
private fun loadCategories() {
viewModelScope.launch {
try {
val response = apiClient.getCategories(
pageSize = 20,
withChildren = false,
withParent = false,
withCount = true,
hideEmpty = true
)
println("分类数据请求完成,响应成功: ${response.isSuccessful}")
if (response.isSuccessful) {
val categoryList = response.body()?.data?.list ?: emptyList()
println("获取到 ${categoryList.size} 个分类")
categories = categoryList.map { category ->
CategoryItem.fromCategoryTemplate(category)
}
println("成功处理并映射了 ${categories.size} 个分类")
} else {
errorMessage = "获取分类数据失败: ${response.code()}"
println("获取分类数据失败: ${response.code()}")
}
} catch (e: Exception) {
errorMessage = "获取分类数据失败: ${e.message}"
println("获取分类数据异常: ${e.message}")
e.printStackTrace()
}
}
}
fun loadAgentsByCategory(categoryId: Int) {
loadAgentData(categoryId)
}
fun loadAllAgents() {
loadAgentData()
}
/**
* 加载更多Agent数据
*/
fun loadMoreAgents() {
// 检查是否正在加载或没有更多数据
if (isLoadingMore || !hasMoreData) {
return
}
val nextPage = currentPage + 1
loadAgentData(
categoryId = currentCategoryId,
page = nextPage,
isLoadMore = true
)
}
fun createSingleChat(
openId: String,
) {
@@ -119,6 +233,29 @@ object AgentViewModel: ViewModel() {
errorMessage = null
isRefreshing = false
isLoading = false
isLoadingMore = false
currentPage = 1
hasMoreData = true
currentCategoryId = null
}
}
}
data class CategoryItem(
val id: Int,
val name: String,
val description: String,
val avatar: String,
val promptCount: Int?
) {
companion object {
fun fromCategoryTemplate(template: CategoryTemplate): CategoryItem {
return CategoryItem(
id = template.id,
name = template.name,
description = template.description,
avatar = "${ApiClient.BASE_API_URL}${template.avatar}",
promptCount = template.promptCount
)
}
}
}

View File

@@ -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)
)

View File

@@ -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))
}
}
) {

View File

@@ -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(

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 773 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 927 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 535 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 975 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 912 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 816 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 966 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1022 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 B

View File

@@ -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>

View File

@@ -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>