用户信息调整

This commit is contained in:
weber
2025-08-26 17:02:34 +08:00
parent 5d4a95bf07
commit 52e571da01
16 changed files with 394 additions and 150 deletions

View File

@@ -42,6 +42,7 @@ 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.comment.ScreenHeader2
import com.aiosman.ravenow.ui.composables.ActionButton
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
import com.aiosman.ravenow.ui.composables.StatusBarSpacer
@@ -96,7 +97,7 @@ fun AddAgentScreen() {
modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp)
.background(color = appColors.decentBackground)
) {
ScreenHeader (
ScreenHeader2 (
title = stringResource(R.string.agent_add),
moreIcon = false
) {
@@ -207,7 +208,7 @@ fun AddAgentScreen() {
backgroundColor = Color.Transparent,
text = stringResource(R.string.agent_create),
isLoading = model.isUpdating,
loadingText = "创建中...",
loadingText = stringResource(R.string.agent_createing),
enabled = !model.isUpdating && model.validate() == null
) {
// 验证输入

View File

@@ -157,6 +157,52 @@ fun ScreenHeader(
}
}
@Composable
fun ScreenHeader2(
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_back_icon,),
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.W600,
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
fun CommentsItem() {
Box(

View File

@@ -134,8 +134,8 @@ fun Agent() {
Column(
modifier = Modifier
.fillMaxWidth()
.height(280.dp)
.padding(vertical = 16.dp)
.height(260.dp)
.padding(vertical = 8.dp)
) {
// 标题
@@ -162,7 +162,7 @@ fun Agent() {
AgentViewPagerSection(agentItems = viewModel.agentItems.take(9),viewModel)
}
Spacer(modifier = Modifier.height(5.dp))
Spacer(modifier = Modifier.height(0.dp))
Row(
modifier = Modifier
.fillMaxWidth()
@@ -171,6 +171,20 @@ fun Agent() {
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.weight(1f))
TabItem(
text = stringResource(R.string.agent_mine),
isSelected = pagerState.currentPage == 0,

View File

@@ -23,8 +23,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@@ -69,16 +67,6 @@ fun HotAgent() {
}
}
val context = LocalContext.current
// 当智能体列表加载完成后,预加载图片
LaunchedEffect(agentList) {
if (agentList.isNotEmpty()) {
model.preloadImages(context)
}
}
Column(
modifier = Modifier
.fillMaxSize()
@@ -122,7 +110,7 @@ fun HotAgent() {
) {
items(
agentList.size,
key = { idx -> agentList[idx].id } // 使用智能体ID作为key避免重新创建
key = { idx -> idx }
) { idx ->
val agentItem = agentList[idx]
AgentCard(

View File

@@ -8,6 +8,7 @@ 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.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.PullRefreshIndicator
@@ -23,16 +24,19 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.LoadState
import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.LocalNavController
import com.aiosman.ravenow.R
import com.aiosman.ravenow.ui.composables.AgentCard
import com.aiosman.ravenow.ui.index.tabs.message.tab.GroupChatItem
import com.aiosman.ravenow.ui.index.tabs.message.tab.GroupChatListViewModel
import java.util.UUID
@OptIn(ExperimentalMaterialApi::class)
@Composable
@@ -40,34 +44,21 @@ fun MineAgent() {
val AppColors = LocalAppTheme.current
val navController = LocalNavController.current
val model = MineAgentViewModel
val agentList = model.agentList.collectAsLazyPagingItems()
val scope = rememberCoroutineScope()
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
model.refreshPager(
//pullRefresh = true
)
//model.refreshPager(pullRefresh = true)
})
val listState = rememberLazyListState()
var dataFlow = model.agentList
var agentList = dataFlow.collectAsLazyPagingItems()
// observe list scrolling
val reachedBottom by remember {
derivedStateOf {
val lastVisibleItem = listState.layoutInfo.visibleItemsInfo.lastOrNull()
lastVisibleItem?.index != 0 && lastVisibleItem?.index == listState.layoutInfo.totalItemsCount - 2
}
}
// Paging 库会自动处理加载更多,无需手动监听滚动
// load more if scrolled to bottom
LaunchedEffect(reachedBottom) {
if (reachedBottom) {
// 只在首次加载时刷新避免从AddAgent返回时重复刷新
LaunchedEffect(Unit) {
if (agentList.itemCount == 0 && !model.isLoading) {
model.refreshPager()
}
}
LaunchedEffect(Unit) {
model.refreshPager()
}
Column(
modifier = Modifier
@@ -110,21 +101,21 @@ fun MineAgent() {
modifier = Modifier.fillMaxSize(),
state = listState
) {
items(agentList.itemCount) { index ->
items(count = agentList.itemCount, key = { index -> agentList[index]?.id ?: index }) { index ->
agentList[index]?.let { agent ->
AgentCard(
agentEntity = agent,
onClick = {
model.createSingleChat(agent.openId)
model.goToChatAi(agent.openId, navController)
model.goToChatAi(agent.openId,navController)
},
)
)
}
}
// 加载更多指示器
if (model.isLoading && agentList.itemCount != 0) {
if (agentList.loadState.append is LoadState.Loading) {
item {
Box(
modifier = Modifier

View File

@@ -31,6 +31,7 @@ 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.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@@ -93,8 +94,8 @@ fun MomentsList() {
) {
Text(
text = stringResource(R.string.index_worldwide),
fontSize = 16.sp,
color = if (pagerState.currentPage == 0) AppColors.text else AppColors.nonActiveText,
fontSize = if (pagerState.currentPage == 0)18.sp else 16.sp,
color = if (pagerState.currentPage == 0) AppColors.text else Color(0X993c3c43),
fontWeight = FontWeight.W600)
Spacer(modifier = Modifier.height(4.dp))
@@ -123,8 +124,8 @@ fun MomentsList() {
) {
Text(
text = stringResource(R.string.index_dynamic),
fontSize = 16.sp,
color = if (pagerState.currentPage == 1) AppColors.text else AppColors.nonActiveText,
fontSize = if (pagerState.currentPage == 1)18.sp else 16.sp,
color = if (pagerState.currentPage == 1) AppColors.text else Color(0X993c3c43),
fontWeight = FontWeight.W600)
Spacer(modifier = Modifier.height(4.dp))
@@ -153,8 +154,8 @@ fun MomentsList() {
) {
Text(
text = stringResource(R.string.index_following),
fontSize = 16.sp,
color = if (pagerState.currentPage == 2) AppColors.text else AppColors.nonActiveText,
fontSize = if (pagerState.currentPage == 2)18.sp else 16.sp,
color = if (pagerState.currentPage == 2) AppColors.text else Color(0X993c3c43),
fontWeight = FontWeight.W600)
Spacer(modifier = Modifier.height(4.dp))
@@ -184,8 +185,8 @@ fun MomentsList() {
) {
Text(
text = stringResource(R.string.index_hot),
fontSize = 16.sp,
color = if (pagerState.currentPage == 3) AppColors.text else AppColors.nonActiveText,
fontSize = if (pagerState.currentPage == 3)18.sp else 16.sp,
color = if (pagerState.currentPage == 3) AppColors.text else Color(0X993c3c43),
fontWeight = FontWeight.W600)
Spacer(modifier = Modifier.height(4.dp))

View File

@@ -326,7 +326,7 @@ fun Explore() {
.padding(horizontal = 4.dp)
.size(3.dp)
.background(
color = if (pagerState.currentPage == index) AppColors.main else AppColors.secondaryText.copy(alpha = 0.3f),
color = if (pagerState.currentPage == index) AppColors.text else AppColors.secondaryText.copy(alpha = 0.3f),
shape = androidx.compose.foundation.shape.CircleShape
)
)
@@ -876,7 +876,7 @@ fun Explore() {
.padding(horizontal = 4.dp)
.size(3.dp)
.background(
color = if (pagerState.currentPage == index) AppColors.main else AppColors.secondaryText.copy(alpha = 0.3f),
color = if (pagerState.currentPage == index) AppColors.text else AppColors.secondaryText.copy(alpha = 0.3f),
shape = androidx.compose.foundation.shape.CircleShape
)
)

View File

@@ -24,9 +24,8 @@ import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
@@ -84,13 +83,14 @@ import com.aiosman.ravenow.ui.composables.toolbar.ScrollStrategy
import com.aiosman.ravenow.ui.composables.toolbar.rememberCollapsingToolbarScaffoldState
import com.aiosman.ravenow.ui.index.IndexViewModel
import com.aiosman.ravenow.ui.index.tabs.profile.composable.EmptyMomentPostUnit
import com.aiosman.ravenow.ui.index.tabs.profile.composable.GalleryItem
import com.aiosman.ravenow.ui.index.tabs.profile.composable.MomentPostUnit
import com.aiosman.ravenow.ui.index.tabs.profile.composable.OtherProfileAction
import com.aiosman.ravenow.ui.index.tabs.profile.composable.SelfProfileAction
import com.aiosman.ravenow.ui.index.tabs.profile.composable.UserAgentsRow
import com.aiosman.ravenow.ui.index.tabs.profile.composable.UserContentPageIndicator
import com.aiosman.ravenow.ui.index.tabs.profile.composable.UserItem
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import com.aiosman.ravenow.ui.navigateToPost
import com.aiosman.ravenow.ui.post.NewPostViewModel
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.delay
@@ -156,18 +156,7 @@ fun ProfileV3(
}
}
val gridState = rememberLazyStaggeredGridState()
val reachedGridBottom by remember {
derivedStateOf {
val lastVisibleItem = gridState.layoutInfo.visibleItemsInfo.lastOrNull()
lastVisibleItem?.index != 0 && lastVisibleItem?.index == gridState.layoutInfo.totalItemsCount - 2
}
}
LaunchedEffect(reachedGridBottom) {
if (reachedGridBottom) {
onLoadMore()
}
}
fun switchTheme() {
// delay
@@ -357,6 +346,12 @@ fun ProfileV3(
}
}
// 添加用户智能体行
UserAgentsRow(
userId = profile?.id,
modifier = Modifier.padding(top = 16.dp)
)
}
}
@@ -461,63 +456,48 @@ fun ProfileV3(
) { idx ->
when (idx) {
0 ->
LazyVerticalStaggeredGrid(
columns = StaggeredGridCells.Fixed(2),
modifier = Modifier.fillMaxSize(),
horizontalArrangement = Arrangement.spacedBy(
8.dp
),
verticalItemSpacing = 8.dp,
contentPadding = PaddingValues(8.dp),
state = gridState
LazyVerticalGrid(
columns = GridCells.Fixed(3),
modifier = Modifier.fillMaxSize().padding(bottom = 8.dp),
) {
if (isSelf) {
items(1) {
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(0.75f)
.clip(
RoundedCornerShape(8.dp)
items(moments.size) { idx ->
val moment = moments[idx] ?: return@items
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f)
.padding(2.dp)
.noRippleClickable {
navController.navigateToPost(
id = moment.id,
highlightCommentId = 0,
initImagePagerIndex = 0
)
.background(
AppColors.background
)
.padding(8.dp)
.noRippleClickable {
NewPostViewModel.asNewPost()
navController.navigate(
NavigationRoute.NewPost.route
)
}
) {
}
) {
CustomAsyncImage(
imageUrl = moment.images[0].thumbnail,
contentDescription = "",
modifier = Modifier.fillMaxSize(),
context = LocalContext.current
)
if (moment.images.size > 1) {
Box(
modifier = Modifier
.fillMaxSize()
.clip(
RoundedCornerShape(8.dp)
)
.background(
AppColors.decentBackground
)
.padding(top = 8.dp, end = 8.dp)
.align(Alignment.TopEnd)
) {
Icon(
Icons.Default.Add,
Image(
modifier = Modifier.size(24.dp),
painter = painterResource(R.drawable.rider_pro_picture_more),
contentDescription = "",
modifier = Modifier
.size(32.dp)
.align(Alignment.Center),
tint = AppColors.text
)
}
}
}
}
items(moments.size) { idx ->
val moment = moments[idx] ?: return@items
GalleryItem(moment, idx)
}
items(2) {
item {
Spacer(modifier = Modifier.height(120.dp))
}
}

View File

@@ -0,0 +1,124 @@
package com.aiosman.ravenow.ui.index.tabs.profile.composable
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.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.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.lifecycle.viewmodel.compose.viewModel
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.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.entity.AgentEntity
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
@Composable
fun UserAgentsRow(
userId: Int?,
modifier: Modifier = Modifier
) {
val AppColors = LocalAppTheme.current
val viewModel: UserAgentsViewModel = viewModel()
// 加载用户的智能体数据
LaunchedEffect(userId) {
if (userId != null) {
viewModel.loadUserAgents(userId)
} else {
viewModel.clearAgents()
}
}
if (viewModel.agents.isNotEmpty()) {
Column(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
Text(
text = "我的智能体",
fontSize = 16.sp,
fontWeight = FontWeight.W600,
color = AppColors.text,
modifier = Modifier.padding(bottom = 12.dp)
)
LazyRow(
horizontalArrangement = Arrangement.spacedBy(12.dp),
modifier = Modifier.fillMaxWidth()
) {
items(viewModel.agents) { agent ->
AgentItem(agent = agent)
}
}
Spacer(modifier = Modifier.height(16.dp))
}
}
}
@Composable
private fun AgentItem(
agent: AgentEntity,
modifier: Modifier = Modifier
) {
val AppColors = LocalAppTheme.current
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier
) {
// 头像
Box(
modifier = Modifier
.size(48.dp)
.clip(CircleShape)
) {
CustomAsyncImage(
context = LocalContext.current,
imageUrl = agent.avatar,
contentDescription = agent.title,
modifier = Modifier.size(48.dp),
contentScale = ContentScale.Crop,
defaultRes = com.aiosman.ravenow.R.mipmap.rider_pro_agent
)
}
Spacer(modifier = Modifier.height(8.dp))
// 名字
Text(
text = agent.title,
fontSize = 12.sp,
fontWeight = FontWeight.W500,
color = AppColors.text,
textAlign = TextAlign.Center,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.width(48.dp)
)
}
}

View File

@@ -0,0 +1,49 @@
package com.aiosman.ravenow.ui.index.tabs.profile.composable
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.entity.AgentEntity
import com.aiosman.ravenow.entity.AgentLoader
import com.aiosman.ravenow.entity.AgentLoaderExtraArgs
import kotlinx.coroutines.launch
class UserAgentsViewModel : ViewModel() {
private val agentLoader = AgentLoader().apply {
onListChanged = {
agents = it
}
}
var agents by mutableStateOf<List<AgentEntity>>(emptyList())
private set
var isLoading by mutableStateOf(false)
private set
var error by mutableStateOf<String?>(null)
private set
fun loadUserAgents(userId: Int) {
viewModelScope.launch {
isLoading = true
error = null
try {
agentLoader.loadData(AgentLoaderExtraArgs(authorId = userId))
} catch (e: Exception) {
error = e.message ?: "加载失败"
} finally {
isLoading = false
}
}
}
fun clearAgents() {
agents = emptyList()
error = null
agentLoader.clear()
}
}