实现了 Agent 和 Profile 数据类,添加了 AddAgent 界面

This commit is contained in:
weber
2025-07-31 15:22:34 +08:00
parent 6ec732b996
commit 29d2bb753f
28 changed files with 1181 additions and 72 deletions

View File

@@ -51,7 +51,7 @@ android {
compose = true compose = true
} }
composeOptions { composeOptions {
kotlinCompilerExtensionVersion = "1.5.1" kotlinCompilerExtensionVersion = "1.5.3"
} }
packaging { packaging {
resources { resources {

View File

@@ -23,6 +23,7 @@ open class AppThemeData(
var inputHint: Color, var inputHint: Color,
var error: Color, var error: Color,
var checkedBackground: Color, var checkedBackground: Color,
var unCheckedBackground: Color,
var checkedText: Color, var checkedText: Color,
var chatActionColor: Color, var chatActionColor: Color,
var brandColorsColor: Color, var brandColorsColor: Color,
@@ -45,6 +46,7 @@ class LightThemeColors : AppThemeData(
inputHint = Color(0xffdadada), inputHint = Color(0xffdadada),
error = Color(0xffFF0000), error = Color(0xffFF0000),
checkedBackground = Color(0xff000000), checkedBackground = Color(0xff000000),
unCheckedBackground = Color(0xFFECEAEC),
checkedText = Color(0xffFFFFFF), checkedText = Color(0xffFFFFFF),
decentBackground = Color(0xfff5f5f5), decentBackground = Color(0xfff5f5f5),
chatActionColor = Color(0xffe0e0e0), chatActionColor = Color(0xffe0e0e0),
@@ -69,6 +71,7 @@ class DarkThemeColors : AppThemeData(
inputHint = Color(0xff888888), inputHint = Color(0xff888888),
error = Color(0xffFF0000), error = Color(0xffFF0000),
checkedBackground = Color(0xffffffff), checkedBackground = Color(0xffffffff),
unCheckedBackground = Color(0xFF7C7480),
checkedText = Color(0xff000000), checkedText = Color(0xff000000),
decentBackground = Color(0xFF171717), decentBackground = Color(0xFF171717),
chatActionColor = Color(0xFF3D3D3D), chatActionColor = Color(0xFF3D3D3D),

View File

@@ -0,0 +1,98 @@
package com.aiosman.ravenow.data
import com.aiosman.ravenow.data.api.ApiClient
import com.aiosman.ravenow.entity.AgentEntity
import com.aiosman.ravenow.entity.ProfileEntity
import com.google.gson.annotations.SerializedName
data class Agent(
@SerializedName("author")
val author: String,
@SerializedName("avatar")
val avatar: String,
@SerializedName("breakMode")
val breakMode: Boolean,
@SerializedName("createdAt")
val createdAt: String,
@SerializedName("desc")
val desc: String,
@SerializedName("id")
val id: Int,
@SerializedName("isPublic")
val isPublic: Boolean,
@SerializedName("openId")
val openId: String,
@SerializedName("profile")
val profile: Profile,
@SerializedName("title")
val title: String,
@SerializedName("updatedAt")
val updatedAt: String,
@SerializedName("useCount")
val useCount: Int
) {
fun toAgentEntity(): AgentEntity {
return AgentEntity(
id = id,
title = title,
desc = desc,
createdAt = createdAt,
updatedAt = updatedAt,
avatar = "${ApiClient.BASE_SERVER}$avatar",
author = author,
isPublic = isPublic,
openId = openId,
breakMode = breakMode,
useCount = useCount,
profile = profile.toProfileEntity(),
)
}
}
data class Profile(
@SerializedName("aiAccount")
val aiAccount: Boolean,
@SerializedName("avatar")
val avatar: String,
@SerializedName("banner")
val banner: String,
@SerializedName("bio")
val bio: String,
@SerializedName("chatAIId")
val chatAIId: String,
@SerializedName("id")
val id: Int,
@SerializedName("nickname")
val nickname: String,
@SerializedName("trtcUserId")
val trtcUserId: String,
@SerializedName("username")
val username: String
){
fun toProfileEntity(): ProfileEntity {
return ProfileEntity(
id = id,
username = username,
nickname = nickname,
avatar = "${ApiClient.BASE_SERVER}$avatar",
bio = bio,
banner = "${ApiClient.BASE_SERVER}$banner",
trtcUserId = trtcUserId,
chatAIId = chatAIId,
aiAccount = aiAccount
)
}
}
interface AgentService {
/**
* 获取智能体列表
*/
suspend fun getAgent(
pageNumber: Int,
pageSize: Int = 20
): ListContainer<AgentEntity>
}

View File

@@ -5,6 +5,7 @@ import com.aiosman.ravenow.data.AccountFollow
import com.aiosman.ravenow.data.AccountLike import com.aiosman.ravenow.data.AccountLike
import com.aiosman.ravenow.data.AccountNotice import com.aiosman.ravenow.data.AccountNotice
import com.aiosman.ravenow.data.AccountProfile import com.aiosman.ravenow.data.AccountProfile
import com.aiosman.ravenow.data.Agent
import com.aiosman.ravenow.data.Comment import com.aiosman.ravenow.data.Comment
import com.aiosman.ravenow.data.DataContainer import com.aiosman.ravenow.data.DataContainer
import com.aiosman.ravenow.data.ListContainer import com.aiosman.ravenow.data.ListContainer
@@ -459,5 +460,18 @@ interface RaveNowAPI {
@Body body: RemoveAccountRequestBody @Body body: RemoveAccountRequestBody
): Response<Unit> ): Response<Unit>
@GET("outside/prompts")
suspend fun getAgent(
@Query("page") page: Int = 1,
@Query("pageSize") pageSize: Int = 20,
): Response<ListContainer<Agent>>
@GET("outside/my/prompts")
suspend fun getMyAgent(
@Query("page") page: Int = 1,
@Query("pageSize") pageSize: Int = 20,
): Response<ListContainer<Agent>>
} }

View File

@@ -0,0 +1,72 @@
package com.aiosman.ravenow.entity
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.aiosman.ravenow.data.ListContainer
import com.aiosman.ravenow.data.AgentService
import com.aiosman.ravenow.data.ServiceException
import com.aiosman.ravenow.data.api.ApiClient
import java.io.IOException
/**
* 智能体
*/
data class AgentEntity(
val author: String,
val avatar: String,
val breakMode: Boolean,
val createdAt: String,
val desc: String,
val id: Int,
val isPublic: Boolean,
val openId: String,
val profile: ProfileEntity,
val title: String,
val updatedAt: String,
val useCount: Int
)
data class ProfileEntity(
val aiAccount: Boolean,
val avatar: String,
val banner: String,
val bio: String,
val chatAIId: String,
val id: Int,
val nickname: String,
val trtcUserId: String,
val username: String
)
class AgentLoaderExtraArgs(
)
class AgentLoader : DataLoader<AgentEntity,AgentLoaderExtraArgs>() {
override suspend fun fetchData(
page: Int,
pageSize: Int,
extra: AgentLoaderExtraArgs
): ListContainer<AgentEntity> {
val result = ApiClient.api.getAgent(
page = page,
pageSize = pageSize,
)
val data = result.body()?.let {
ListContainer(
list = it.list.map { it.toAgentEntity()},
total = it.total,
page = page,
pageSize = pageSize
)
}
if (data == null) {
throw ServiceException("Failed to get agent")
}
return data
}
}

View File

@@ -31,6 +31,7 @@ import com.aiosman.ravenow.ui.about.AboutScreen
import com.aiosman.ravenow.ui.account.AccountEditScreen2 import com.aiosman.ravenow.ui.account.AccountEditScreen2
import com.aiosman.ravenow.ui.account.AccountSetting import com.aiosman.ravenow.ui.account.AccountSetting
import com.aiosman.ravenow.ui.account.ResetPasswordScreen import com.aiosman.ravenow.ui.account.ResetPasswordScreen
import com.aiosman.ravenow.ui.agent.AddAgentScreen
import com.aiosman.ravenow.ui.chat.ChatScreen import com.aiosman.ravenow.ui.chat.ChatScreen
import com.aiosman.ravenow.ui.comment.CommentsScreen import com.aiosman.ravenow.ui.comment.CommentsScreen
import com.aiosman.ravenow.ui.comment.notice.CommentNoticeScreen import com.aiosman.ravenow.ui.comment.notice.CommentNoticeScreen
@@ -94,6 +95,7 @@ sealed class NavigationRoute(
data object ImageCrop : NavigationRoute("ImageCrop") data object ImageCrop : NavigationRoute("ImageCrop")
data object AccountSetting : NavigationRoute("AccountSetting") data object AccountSetting : NavigationRoute("AccountSetting")
data object AboutScreen : NavigationRoute("AboutScreen") data object AboutScreen : NavigationRoute("AboutScreen")
data object AddAgent : NavigationRoute("AddAgent")
} }
@@ -395,6 +397,17 @@ fun NavigationController(
AboutScreen() AboutScreen()
} }
} }
composable(
route = NavigationRoute.AddAgent.route,
enterTransition = {
fadeIn(animationSpec = tween(durationMillis = 0))
},
exitTransition = {
fadeOut(animationSpec = tween(durationMillis = 0))
}
) {
AddAgentScreen()
}
} }
} }

View File

@@ -0,0 +1,179 @@
package com.aiosman.ravenow.ui.agent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
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.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
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.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewModelScope
import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.LocalNavController
import com.aiosman.ravenow.R
import com.aiosman.ravenow.ui.NavigationRoute
import com.aiosman.ravenow.ui.account.AccountEditViewModel
import com.aiosman.ravenow.ui.comment.NoticeScreenHeader
import com.aiosman.ravenow.ui.comment.ScreenHeader
import com.aiosman.ravenow.ui.composables.ActionButton
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
import com.aiosman.ravenow.ui.composables.StatusBarSpacer
import com.aiosman.ravenow.ui.composables.form.FormTextInput
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
/**
* 添加智能体界面
*/
@Composable
fun AddAgentScreen() {
val model = AddAgentViewModel
val navController = LocalNavController.current
val context = LocalContext.current
var agnetNameError by remember { mutableStateOf<String?>(null) }
var agnetDescError by remember { mutableStateOf<String?>(null) }
fun onNicknameChange(value: String) {
model.name = value
agnetNameError = when {
/*value.isEmpty() -> "昵称不能为空"
value.length < 3 -> "昵称长度不能小于3"
value.length > 20 -> "昵称长度不能大于20"*/
else -> null
}
}
val appColors = LocalAppTheme.current
fun onDescChange(value: String) {
model.desc = value
agnetDescError = when {
value.length > 100 -> "个人简介长度不能大于24"
else -> null
}
}
fun validate(): Boolean {
return agnetNameError == null && agnetDescError == null
}
Column(
modifier = Modifier
.fillMaxSize()
.background(color = appColors.background),
horizontalAlignment = Alignment.CenterHorizontally
) {
StatusBarSpacer()
Box(
modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp)
) {
ScreenHeader (
title = stringResource(R.string.agent_add),
moreIcon = false
) {
Icon(
modifier = Modifier
.size(24.dp)
.noRippleClickable {
},
imageVector = Icons.Default.Check,
contentDescription = "Add")
}
}
Spacer(modifier = Modifier.height(44.dp))
Box(
modifier = Modifier.size(88.dp),
contentAlignment = Alignment.Center
) {
CustomAsyncImage(
context,
"",
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 {
navController.navigate(NavigationRoute.ImageCrop.route)
},
contentAlignment = Alignment.Center
) {
Icon(
Icons.Default.Add,
contentDescription = "Add",
tint = Color.White,
)
}
}
Spacer(modifier = Modifier.height(58.dp))
Column(
modifier = Modifier
.padding(horizontal = 16.dp)
) {
FormTextInput(
value = model.name,
label = stringResource(R.string.agent_name),
hint = stringResource(R.string.agent_name_hint),
modifier = Modifier.fillMaxWidth(),
) { value ->
onNicknameChange(value)
}
// Spacer(modifier = Modifier.height(16.dp))
FormTextInput(
value = model.desc,
label = stringResource(R.string.agent_desc),
hint = stringResource(R.string.agent_desc_hint),
modifier = Modifier.fillMaxWidth(),
) { value ->
onDescChange(value)
}
}
Spacer(modifier = Modifier.height(58.dp))
ActionButton(
modifier = Modifier
.width(345.dp),
text = stringResource(R.string.agent_create),
) {
}
}
}

View File

@@ -0,0 +1,25 @@
package com.aiosman.ravenow.ui.agent
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.aiosman.ravenow.data.AccountService
import com.aiosman.ravenow.data.AccountServiceImpl
import com.aiosman.ravenow.data.UploadImage
import com.aiosman.ravenow.entity.AccountProfileEntity
import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel
import com.aiosman.ravenow.utils.TrtcHelper
import java.io.File
object AddAgentViewModel : ViewModel() {
var name by mutableStateOf("")
var desc by mutableStateOf("")
var imageUrl by mutableStateOf<Uri?>(null)
var croppedBitmap by mutableStateOf<Bitmap?>(null)
var isUpdating by mutableStateOf(false)
}

View File

@@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -23,6 +24,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
@@ -77,7 +79,7 @@ fun NoticeScreenHeader(
Image( Image(
painter = painterResource(id = R.drawable.rider_pro_back_icon,), painter = painterResource(id = R.drawable.rider_pro_back_icon,),
contentDescription = title, contentDescription = title,
modifier = Modifier.size(16.dp).clickable( modifier = Modifier.size(24.dp).clickable(
indication = null, indication = null,
interactionSource = remember { MutableInteractionSource() } interactionSource = remember { MutableInteractionSource() }
) { ) {
@@ -102,6 +104,52 @@ fun NoticeScreenHeader(
} }
} }
@Composable
fun ScreenHeader(
title:String,
moreIcon: Boolean = true,
rightIcon: @Composable (() -> Unit)? = null
) {
val nav = LocalNavController.current
val AppColors = LocalAppTheme.current
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
Image(
painter = painterResource(id = R.drawable.rider_pro_close,),
contentDescription = title,
modifier = Modifier.size(24.dp).clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
nav.navigateUp()
},
colorFilter = ColorFilter.tint(AppColors.text)
)
Spacer(modifier = Modifier.size(12.dp))
Text(title,
fontWeight = FontWeight.W800,
modifier = Modifier.weight(1f),
textAlign = TextAlign.Center,
fontSize = 17.sp,
color = AppColors.text)
Spacer(modifier = Modifier.size(12.dp))
if (moreIcon) {
Spacer(modifier = Modifier.weight(1f))
Image(
painter = painterResource(id = R.drawable.rider_pro_more_horizon),
contentDescription = "More",
modifier = Modifier
.size(24.dp),
)
}
if (rightIcon != null) {
//rightIcon()
}
}
}
@Composable @Composable
fun CommentsItem() { fun CommentsItem() {
Box( Box(

View File

@@ -0,0 +1,108 @@
package com.aiosman.ravenow.ui.composables
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
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.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.LocalNavController
import com.aiosman.ravenow.entity.AgentEntity
import com.aiosman.ravenow.entity.MomentEntity
import com.aiosman.ravenow.exp.timeAgo
import com.aiosman.ravenow.ui.NavigationRoute
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
@Composable
fun AgentCard(
modifier: Modifier = Modifier,
agentEntity: AgentEntity,
) {
val AppColors = LocalAppTheme.current
val context = LocalContext.current
Column(
modifier = modifier
.fillMaxWidth()
.background(AppColors.background)
) {
Box(
modifier = Modifier.padding(start = 0.dp, end = 0.dp, top = 16.dp, bottom = 8.dp)
) {
Row(
modifier = Modifier
) {
CustomAsyncImage(
context,
agentEntity.avatar,
contentDescription = "",
modifier = Modifier
.size(40.dp)
.clip(RoundedCornerShape(40.dp))
,
contentScale = ContentScale.Crop
)
Column(
modifier = Modifier
.weight(1f)
.padding(start = 12.dp, end = 12.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(22.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
modifier = Modifier.weight(1f),
textAlign = TextAlign.Start,
text = agentEntity.title,
color = AppColors.text,
fontSize = 16.sp, style = TextStyle(fontWeight = FontWeight.Bold)
)
}
Spacer(modifier = Modifier.width(16.dp))
}
Row(
modifier = Modifier
.fillMaxWidth()
.height(21.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
modifier = Modifier,
text = agentEntity.desc,
color = AppColors.text,
fontSize = 12.sp
)
Spacer(modifier = Modifier.width(8.dp))
//MomentPostLocation(momentEntity.location)
}
}
}
}
}

View File

@@ -212,7 +212,8 @@ fun MomentPostLocation(location: String) {
Text( Text(
text = location, text = location,
color = AppColors.secondaryText, color = AppColors.secondaryText,
fontSize = 12.sp fontSize = 12.sp,
) )
} }
@@ -280,7 +281,7 @@ fun MomentTopRowGroup(
) { ) {
MomentPostTime(momentEntity.time.timeAgo(context)) MomentPostTime(momentEntity.time.timeAgo(context))
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
MomentPostLocation(momentEntity.location) //MomentPostLocation(momentEntity.location)
} }
} }
val isFollowing = momentEntity.followStatus val isFollowing = momentEntity.followStatus
@@ -382,7 +383,7 @@ fun MomentOperateBtn(@DrawableRes icon: Int, count: String) {
Text( Text(
text = count, text = count,
modifier = Modifier.padding(start = 7.dp), modifier = Modifier.padding(start = 7.dp),
fontSize = 12.sp, fontSize = 14.sp,
color = AppColors.text color = AppColors.text
) )
} }

View File

@@ -61,6 +61,7 @@ import com.aiosman.ravenow.Messaging
import com.aiosman.ravenow.R import com.aiosman.ravenow.R
import com.aiosman.ravenow.ui.NavigationRoute import com.aiosman.ravenow.ui.NavigationRoute
import com.aiosman.ravenow.ui.index.tabs.add.AddPage import com.aiosman.ravenow.ui.index.tabs.add.AddPage
import com.aiosman.ravenow.ui.index.tabs.ai.Agent
import com.aiosman.ravenow.ui.index.tabs.message.NotificationsScreen import com.aiosman.ravenow.ui.index.tabs.message.NotificationsScreen
import com.aiosman.ravenow.ui.index.tabs.moment.MomentsList import com.aiosman.ravenow.ui.index.tabs.moment.MomentsList
import com.aiosman.ravenow.ui.index.tabs.profile.ProfileWrap import com.aiosman.ravenow.ui.index.tabs.profile.ProfileWrap
@@ -332,7 +333,7 @@ fun IndexScreen() {
) { page -> ) { page ->
when (page) { when (page) {
0 -> Home() 0 -> Home()
1 -> DiscoverScreen() 1 -> Agent()
2 -> Add() 2 -> Add()
3 -> Notifications() 3 -> Notifications()
4 -> Profile() 4 -> Profile()

View File

@@ -0,0 +1,250 @@
package com.aiosman.ravenow.ui.index.tabs.ai
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
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.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.pager.HorizontalPager
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.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.LocalNavController
import com.aiosman.ravenow.R
import com.aiosman.ravenow.ui.NavigationRoute
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import kotlinx.coroutines.launch
@OptIn( ExperimentalFoundationApi::class)
@Composable
fun Agent() {
val AppColors = LocalAppTheme.current
val navController = LocalNavController.current
val navigationBarPaddings =
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
var pagerState = rememberPagerState { 4 }
var scope = rememberCoroutineScope()
Column(
modifier = Modifier
.fillMaxSize()
.padding(
top = statusBarPaddingValues.calculateTopPadding()+18.dp,
bottom = navigationBarPaddings,
start = 16.dp,
end = 16.dp
),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Row(
modifier = Modifier
.height(36.dp) // 设置高度为36dp
.fillMaxWidth(), // 占据整行宽度
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.Bottom
) {
// 搜索框 - 占据剩余空间
Row(
modifier = Modifier
.height(36.dp)
.weight(1f) // 权重为1占据剩余空间
.clip(shape = RoundedCornerShape(18.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 {
// 图标点击事件
navController.navigate(
NavigationRoute.AddAgent.route
)
},
painter = painterResource(id = R.drawable.rider_pro_new_post_add_pic),
contentDescription = null,
tint = AppColors.text
)
}
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
// center the tabs
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.Bottom
) {
Column(
modifier = Modifier
.noRippleClickable {
scope.launch {
pagerState.animateScrollToPage(0)
}
},
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = stringResource(R.string.agent_mine),
fontSize = 14.sp,
color = if (pagerState.currentPage == 0) AppColors.mainText else AppColors.checkedBackground,
modifier = Modifier
.clip(RoundedCornerShape(8.dp))
.background(if (pagerState.currentPage == 0) AppColors.checkedBackground else AppColors.unCheckedBackground)
.padding(horizontal = 11.dp, vertical = 4.dp)
)
}
Spacer(modifier = Modifier.width(8.dp))
Column(
modifier = Modifier
.noRippleClickable {
scope.launch {
pagerState.animateScrollToPage(1)
}
},
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = stringResource(R.string.agent_hot),
fontSize = 14.sp,
color = if (pagerState.currentPage == 1) AppColors.mainText else AppColors.checkedBackground,
modifier = Modifier
.clip(RoundedCornerShape(8.dp))
.background(if (pagerState.currentPage == 1) AppColors.checkedBackground else AppColors.unCheckedBackground)
.padding(horizontal = 11.dp, vertical = 4.dp)
)
}
Spacer(modifier = Modifier.width(8.dp))
Column(
modifier = Modifier
.noRippleClickable {
scope.launch {
pagerState.animateScrollToPage(2)
}
},
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = stringResource(R.string.agent_recommend),
fontSize = 14.sp,
color = if (pagerState.currentPage == 2) AppColors.mainText else AppColors.checkedBackground,
modifier = Modifier
.clip(RoundedCornerShape(8.dp))
.background(if (pagerState.currentPage == 2) AppColors.checkedBackground else AppColors.unCheckedBackground)
.padding(horizontal = 11.dp, vertical = 4.dp)
)
}
Spacer(modifier = Modifier.width(8.dp))
Column(
modifier = Modifier
.noRippleClickable {
scope.launch {
pagerState.animateScrollToPage(3)
}
},
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = stringResource(R.string.agent_other),
fontSize = 14.sp,
color = if (pagerState.currentPage == 3) AppColors.mainText else AppColors.checkedBackground,
modifier = Modifier
.clip(RoundedCornerShape(8.dp))
.background(if (pagerState.currentPage == 3) AppColors.checkedBackground else AppColors.unCheckedBackground)
.padding(horizontal = 11.dp, vertical = 4.dp)
)
}
}
Spacer(modifier = Modifier.height(16.dp))
HorizontalPager(
state = pagerState,
modifier = Modifier
.fillMaxWidth()
.weight(1f)
) {
when (it) {
0 -> {
// ExploreMomentsList()
}
1 -> {
//TimelineMomentsList()
}
2 -> {
//HotMomentsList()
}
3 -> {
//MineAgent()
}
}
}
}
}

View File

@@ -0,0 +1,7 @@
package com.aiosman.ravenow.ui.index.tabs.ai
import androidx.lifecycle.ViewModel
object AgentViewModel: ViewModel() {
}

View File

@@ -0,0 +1,59 @@
package com.aiosman.ravenow.ui.index.tabs.ai
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.aiosman.ravenow.data.AgentService
import com.aiosman.ravenow.data.MomentService
import com.aiosman.ravenow.data.UserServiceImpl
import com.aiosman.ravenow.entity.AgentEntity
import com.aiosman.ravenow.entity.AgentLoader
import com.aiosman.ravenow.entity.AgentLoaderExtraArgs
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
open class BaseAgentModel :ViewModel(){
// private val agentService: AgentService = AgentServiceImpl()
val agentLoader = AgentLoader().apply {
onListChanged = {
agentList = it
}
}
var refreshing by mutableStateOf(false)
var isFirstLoad = true
var agentList by mutableStateOf<List<AgentEntity>>(listOf())
open fun extraArgs(): AgentLoaderExtraArgs {
return AgentLoaderExtraArgs()
}
fun refreshPager(pullRefresh: Boolean = false) {
if (!isFirstLoad && !pullRefresh) {
return
}
isFirstLoad = false
agentLoader.clear()
viewModelScope.launch {
agentLoader.loadData(extraArgs())
}
}
fun loadMore() {
viewModelScope.launch {
agentLoader.loadMore(extraArgs())
agentList = agentLoader.list
}
}
fun ResetModel() {
agentLoader.clear()
isFirstLoad = true
}
override fun onCleared() {
super.onCleared()
EventBus.getDefault().unregister(this)
}
}

View File

@@ -0,0 +1,119 @@
package com.aiosman.ravenow.ui.index.tabs.ai.tabs.mine
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.R
import com.aiosman.ravenow.ui.composables.AgentCard
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun MineAgent() {
val AppColors = LocalAppTheme.current
val model = MineAgentViewModel
var agentList = model.agentList
val scope = rememberCoroutineScope()
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
model.refreshPager(
pullRefresh = true
)
})
val listState = rememberLazyListState()
// observe list scrolling
val reachedBottom by remember {
derivedStateOf {
val lastVisibleItem = listState.layoutInfo.visibleItemsInfo.lastOrNull()
lastVisibleItem?.index != 0 && lastVisibleItem?.index == listState.layoutInfo.totalItemsCount - 2
}
}
// load more if scrolled to bottom
LaunchedEffect(reachedBottom) {
if (reachedBottom) {
model.loadMore()
}
}
LaunchedEffect(Unit) {
model.refreshPager()
}
Column(
modifier = Modifier
.fillMaxSize()
) {
if(agentList.size == 0) {
Box(
modifier = Modifier
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Image(
painter = painterResource(id = R.mipmap.rider_pro_following_empty),
contentDescription = null,
modifier = Modifier.size(140.dp)
)
Spacer(modifier = Modifier.size(32.dp))
androidx.compose.material.Text(
text = "You haven't followed anyone yet",
color = AppColors.text,
fontSize = 16.sp,
fontWeight = FontWeight.W600
)
Spacer(modifier = Modifier.size(16.dp))
androidx.compose.material.Text(
text = "Click start your social journey.",
color = AppColors.text,
fontSize = 16.sp,
fontWeight = FontWeight.W400
)
}
}
}else{
Box(Modifier.pullRefresh(state)) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = listState
) {
items(
agentList.size,
key = { idx -> idx }
) { idx ->
val agentItem = agentList[idx] ?: return@items
AgentCard(agentEntity = agentItem,
)
}
}
PullRefreshIndicator(model.refreshing, state, Modifier.align(Alignment.TopCenter))
}
}
}
}

View File

@@ -0,0 +1,22 @@
package com.aiosman.ravenow.ui.index.tabs.ai.tabs.mine
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import com.aiosman.ravenow.entity.AgentLoaderExtraArgs
import com.aiosman.ravenow.entity.MomentLoaderExtraArgs
import com.aiosman.ravenow.ui.index.tabs.ai.BaseAgentModel
import com.aiosman.ravenow.ui.index.tabs.moment.BaseMomentModel
import org.greenrobot.eventbus.EventBus
object MineAgentViewModel : BaseAgentModel() {
init {
//EventBus.getDefault().register(this)
}
override fun extraArgs(): AgentLoaderExtraArgs {
return AgentLoaderExtraArgs()
}
}

View File

@@ -93,9 +93,12 @@ fun NotificationsScreen() {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp), .padding(horizontal = 15.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
// 左侧占位元素
Box(modifier = Modifier.size(24.dp))
// 左侧 Columnlabel 居中显示 // 左侧 Columnlabel 居中显示
Column( Column(
modifier = Modifier modifier = Modifier
@@ -105,21 +108,24 @@ fun NotificationsScreen() {
) { ) {
Text( Text(
text = stringResource(R.string.main_message), text = stringResource(R.string.main_message),
fontSize = 16.sp, fontSize = 17.sp,
fontWeight = FontWeight.W700,
color = AppColors.text color = AppColors.text
) )
} }
// 右侧图标
// 右侧图片按钮:靠右显示
Image( Image(
painter = painterResource(id = R.drawable.rider_pro_favourite), painter = painterResource(id = R.drawable.rider_pro_group),
contentDescription = stringResource(R.string.main_message), contentDescription = "add",
modifier = Modifier modifier = Modifier
.size(24.dp) .size(24.dp)
.noRippleClickable { .noRippleClickable {
// 点击事件
} },
colorFilter = ColorFilter.tint(AppColors.text)
) )
} }
Row( Row(
modifier = Modifier modifier = Modifier

View File

@@ -53,6 +53,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onGloballyPositioned
@@ -61,6 +62,7 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.ravenow.AppState import com.aiosman.ravenow.AppState
@@ -384,11 +386,22 @@ fun ProfileV3(
), ),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
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( CustomAsyncImage(
LocalContext.current, LocalContext.current,
profile?.avatar, profile?.avatar,
modifier = Modifier modifier = Modifier
.size(48.dp) .size(32.dp)
.clip(CircleShape), .clip(CircleShape),
contentDescription = "", contentDescription = "",
contentScale = ContentScale.Crop contentScale = ContentScale.Crop

View File

@@ -46,6 +46,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.ravenow.AppState import com.aiosman.ravenow.AppState
@@ -69,7 +70,7 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Preview
@Composable @Composable
fun LoginPage() { fun LoginPage() {
val navController = LocalNavController.current val navController = LocalNavController.current

View File

@@ -395,18 +395,6 @@ fun PostScreen(
viewModel.moment viewModel.moment
) )
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Box(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.height(1.dp)
.background(
AppColors.divider
)
) {
}
Spacer(modifier = Modifier.height(24.dp))
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -419,7 +407,8 @@ fun PostScreen(
R.string.comment_count, R.string.comment_count,
(viewModel.moment?.commentCount ?: 0) (viewModel.moment?.commentCount ?: 0)
), fontSize = 14.sp, ), fontSize = 14.sp,
color = AppColors.text fontWeight = FontWeight.Bold,
color = AppColors.nonActiveText
) )
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
OrderSelectionComponent() { OrderSelectionComponent() {
@@ -724,13 +713,13 @@ fun Header(
.noRippleClickable { .noRippleClickable {
navController.navigateUp() navController.navigateUp()
} }
.size(32.dp), .size(24.dp),
colorFilter = ColorFilter.tint(AppColors.text) colorFilter = ColorFilter.tint(AppColors.text)
) )
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
Box( Box(
modifier = Modifier modifier = Modifier
.size(40.dp) .size(32.dp)
.clip(CircleShape) .clip(CircleShape)
.background(AppColors.secondaryText.copy(alpha = 0.1f)) .background(AppColors.secondaryText.copy(alpha = 0.1f))
) { ) {
@@ -739,7 +728,7 @@ fun Header(
avatar, avatar,
contentDescription = "Profile Picture", contentDescription = "Profile Picture",
modifier = Modifier modifier = Modifier
.size(40.dp) .size(32.dp)
.clip(CircleShape) .clip(CircleShape)
.noRippleClickable { .noRippleClickable {
userId?.let { userId?.let {
@@ -761,6 +750,7 @@ fun Header(
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
color = AppColors.text, color = AppColors.text,
fontSize = 17.sp
) )
if (AppState.UserId != userId && !isFollowing) { if (AppState.UserId != userId && !isFollowing) {
FollowButton( FollowButton(
@@ -774,7 +764,7 @@ fun Header(
Box { Box {
Image( Image(
modifier = Modifier modifier = Modifier
.height(20.dp) .height(24.dp)
.padding(end = 8.dp) .padding(end = 8.dp)
.noRippleClickable { .noRippleClickable {
expanded = true expanded = true
@@ -1049,12 +1039,15 @@ fun PostDetails(
if (!momentEntity?.momentTextContent.isNullOrEmpty()) { if (!momentEntity?.momentTextContent.isNullOrEmpty()) {
Text( Text(
text = momentEntity?.momentTextContent ?: "", text = momentEntity?.momentTextContent ?: "",
fontSize = 16.sp, fontSize = 14.sp,
fontWeight = FontWeight.Bold,
color = AppColors.text, color = AppColors.text,
) )
} }
Text(text = "${momentEntity?.time?.formatPostTime()}", color = AppColors.text) Text(
modifier = Modifier.padding(top = 16.dp),
text = "${momentEntity?.time?.formatPostTime()}",
color = AppColors.nonActiveText,
fontSize = 12.sp)
} }
} }
@@ -1084,7 +1077,8 @@ fun CommentItem(
Row(modifier = Modifier.padding(vertical = 8.dp)) { Row(modifier = Modifier.padding(vertical = 8.dp)) {
Box( Box(
modifier = Modifier modifier = Modifier
.size(if (isChild) 24.dp else 40.dp) .size(if (isChild) 24.dp else 32.dp)
.clip(CircleShape)
.background(Color.Gray.copy(alpha = 0.1f)) .background(Color.Gray.copy(alpha = 0.1f))
.noRippleClickable { .noRippleClickable {
navController.navigate( navController.navigate(
@@ -1099,7 +1093,8 @@ fun CommentItem(
context = context, context = context,
imageUrl = commentEntity.avatar, imageUrl = commentEntity.avatar,
contentDescription = "Comment Profile Picture", contentDescription = "Comment Profile Picture",
modifier = Modifier.size(if (isChild) 24.dp else 40.dp), modifier = Modifier.size(if (isChild) 24.dp else 32.dp)
.clip(CircleShape),
contentScale = ContentScale.Crop contentScale = ContentScale.Crop
) )
} }
@@ -1115,13 +1110,21 @@ fun CommentItem(
} }
) {} ) {}
) { ) {
Row {
Text( Text(
text = commentEntity.name, text = commentEntity.name,
fontWeight = FontWeight.W600, fontWeight = FontWeight.Bold,
fontSize = 14.sp, fontSize = 11.sp,
color = AppColors.text color = AppColors.text
) )
Row { 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) { if (isChild) {
val annotatedText = buildAnnotatedString { val annotatedText = buildAnnotatedString {
if (commentEntity.replyUserId != null) { if (commentEntity.replyUserId != null) {
@@ -1169,9 +1172,10 @@ fun CommentItem(
} else { } else {
Text( Text(
text = commentEntity.comment, text = commentEntity.comment,
fontSize = 14.sp, fontSize = 13.sp,
maxLines = Int.MAX_VALUE, maxLines = Int.MAX_VALUE,
softWrap = true, softWrap = true,
lineHeight = 20.sp,
color = AppColors.text, color = AppColors.text,
modifier = Modifier.combinedClickable( modifier = Modifier.combinedClickable(
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
@@ -1187,18 +1191,42 @@ fun CommentItem(
) )
} }
} }
Row { Row (modifier = Modifier.padding(top = 12.dp),
Text( verticalAlignment = Alignment.CenterVertically,){
text = commentEntity.date.timeAgo(context), AnimatedLikeIcon(
fontSize = 12.sp, liked = commentEntity.liked,
color = Color.Gray onClick = {
onLike(commentEntity)
},
modifier = Modifier.size(16.dp)
) )
Spacer(modifier = Modifier.width(8.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))
if (AppState.UserId?.toLong() != commentEntity.author) { if (AppState.UserId?.toLong() != commentEntity.author) {
Icon(
painter = painterResource(id = R.drawable.rider_pro_comment),
contentDescription = "",
modifier = Modifier.size(16.dp),
tint = AppColors.nonActiveText
)
Spacer(modifier = Modifier.width(4.dp))
Text( Text(
text = stringResource(R.string.reply), text = stringResource(R.string.reply),
fontSize = 12.sp, fontSize = 12.sp,
color = AppColors.secondaryText, fontWeight = FontWeight.Bold,
color = AppColors.nonActiveText,
modifier = Modifier.noRippleClickable { modifier = Modifier.noRippleClickable {
onReply( onReply(
commentEntity, commentEntity,
@@ -1213,23 +1241,6 @@ fun CommentItem(
} }
} }
Spacer(modifier = Modifier.width(16.dp))
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
AnimatedLikeIcon(
liked = commentEntity.liked,
onClick = {
onLike(commentEntity)
},
modifier = Modifier.size(20.dp)
)
Text(
text = commentEntity.likes.toString(),
fontSize = 12.sp,
color = AppColors.text
)
}
} }
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Column( Column(
@@ -1267,8 +1278,8 @@ fun CommentItem(
val remaining = commentEntity.replyCount - commentEntity.reply.size val remaining = commentEntity.replyCount - commentEntity.reply.size
Text( Text(
text = stringResource(R.string.view_more_reply, remaining), text = stringResource(R.string.view_more_reply, remaining),
fontSize = 12.sp, fontSize = 14.sp,
color = Color(0xFF6F94AE), color = AppColors.nonActiveText,
modifier = Modifier.noRippleClickable { modifier = Modifier.noRippleClickable {
onLoadMoreSubComments?.invoke(commentEntity) onLoadMoreSubComments?.invoke(commentEntity)
} }

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group
android:translateX="2"
android:translateY="4">
<path
android:fillType="evenOdd"
android:strokeColor="#000"
android:strokeWidth="1.778"
android:pathData="M7.273 10.777c4.016 0 7.272 1.791 7.272 4 0 0.706-0.331 1.368-0.914 1.944-0.392 0.179 -0.828 0.278 -1.288 0.278 H2.202a3.1 3.1 0 0 1-1.288-0.278C0.332 16.145 0 15.483 0 14.777c0-2.209 3.256-4 7.273-4z" />
<path
android:fillType="evenOdd"
android:strokeColor="#000"
android:strokeWidth="1.778"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M16.491 17h1.236c0.5 0 2.273-0.398 2.273-2.223 0-1.49-1.482-2.79-3.68-3.478" />
<path
android:fillType="evenOdd"
android:strokeColor="#000"
android:strokeWidth="1.778"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M 7.273 0 C 9.28110735038 0 10.909 1.59162771435 10.909 3.555 C 10.909 5.51837228565 9.28110735038 7.11 7.273 7.11 C 5.26489264962 7.11 3.637 5.51837228565 3.637 3.555 C 3.637 1.59162771435 5.26489264962 0 7.273 0 Z" />
<path
android:fillType="evenOdd"
android:strokeColor="#000"
android:strokeWidth="1.778"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M13.636 0.116 c1.61 0.402 2.735 1.82 2.735 3.444 0 1.624-1.126 3.041-2.735 3.444" />
</group>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -115,4 +115,15 @@
<string name="main_ai">智能体</string> <string name="main_ai">智能体</string>
<string name="main_message">消息</string> <string name="main_message">消息</string>
<string name="main_profile">我的</string> <string name="main_profile">我的</string>
<string name="agent_mine">我的</string>
<string name="agent_hot">热门</string>
<string name="agent_recommend">推荐</string>
<string name="agent_other">其他</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>
</resources> </resources>

View File

@@ -114,4 +114,14 @@
<string name="main_ai">Agent</string> <string name="main_ai">Agent</string>
<string name="main_message">Message</string> <string name="main_message">Message</string>
<string name="main_profile">Profile</string> <string name="main_profile">Profile</string>
<string name="agent_mine">Mine</string>
<string name="agent_hot">Hot</string>
<string name="agent_recommend">Recommend</string>
<string name="agent_other">Other</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>
</resources> </resources>

View File

@@ -3,7 +3,7 @@ accompanistSystemuicontroller = "0.27.0"
agp = "8.4.0" agp = "8.4.0"
animation = "1.7.0-beta05" animation = "1.7.0-beta05"
composeImageBlurhash = "3.0.2" composeImageBlurhash = "3.0.2"
kotlin = "1.9.0" kotlin = "1.9.10"
coreKtx = "1.10.1" coreKtx = "1.10.1"
junit = "4.13.2" junit = "4.13.2"
junitVersion = "1.1.5" junitVersion = "1.1.5"