首页UI调整
This commit is contained in:
@@ -18,8 +18,8 @@ import com.aiosman.ravenow.ui.favourite.FavouriteNoticeViewModel
|
|||||||
import com.aiosman.ravenow.ui.follower.FollowerNoticeViewModel
|
import com.aiosman.ravenow.ui.follower.FollowerNoticeViewModel
|
||||||
import com.aiosman.ravenow.ui.index.IndexViewModel
|
import com.aiosman.ravenow.ui.index.IndexViewModel
|
||||||
import com.aiosman.ravenow.ui.index.tabs.message.MessageListViewModel
|
import com.aiosman.ravenow.ui.index.tabs.message.MessageListViewModel
|
||||||
|
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.dynamic.DynamicViewModel
|
||||||
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre.Explore
|
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre.Explore
|
||||||
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre.ExploreViewModel
|
|
||||||
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.hot.HotMomentViewModel
|
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.hot.HotMomentViewModel
|
||||||
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.timeline.TimelineMomentViewModel
|
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.timeline.TimelineMomentViewModel
|
||||||
import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel
|
import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel
|
||||||
@@ -152,7 +152,7 @@ object AppState {
|
|||||||
fun ReloadAppState(context: Context) {
|
fun ReloadAppState(context: Context) {
|
||||||
// 重置动态列表页面
|
// 重置动态列表页面
|
||||||
TimelineMomentViewModel.ResetModel()
|
TimelineMomentViewModel.ResetModel()
|
||||||
ExploreViewModel.ResetModel()
|
DynamicViewModel.ResetModel()
|
||||||
HotMomentViewModel.ResetModel()
|
HotMomentViewModel.ResetModel()
|
||||||
|
|
||||||
// 重置我的页面
|
// 重置我的页面
|
||||||
|
|||||||
@@ -110,11 +110,13 @@ fun HotAgent() {
|
|||||||
key = { idx -> idx }
|
key = { idx -> idx }
|
||||||
) { idx ->
|
) { idx ->
|
||||||
val agentItem = agentList[idx]
|
val agentItem = agentList[idx]
|
||||||
AgentCard(agentEntity = agentItem,
|
AgentCard(
|
||||||
|
agentEntity = agentItem,
|
||||||
onClick = {
|
onClick = {
|
||||||
model.createSingleChat(agentItem.openId)
|
model.createSingleChat(agentItem.openId)
|
||||||
model.goToChatAi(agentItem.openId,navController)
|
model.goToChatAi(agentItem.openId,navController)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载更多指示器
|
// 加载更多指示器
|
||||||
|
|||||||
@@ -27,13 +27,10 @@ import androidx.compose.ui.res.painterResource
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.aiosman.ravenow.LocalAppTheme
|
import com.aiosman.ravenow.LocalAppTheme
|
||||||
import com.aiosman.ravenow.LocalNavController
|
import com.aiosman.ravenow.LocalNavController
|
||||||
import com.aiosman.ravenow.R
|
import com.aiosman.ravenow.R
|
||||||
import com.aiosman.ravenow.ui.composables.AgentCard
|
import com.aiosman.ravenow.ui.composables.AgentCard
|
||||||
import com.aiosman.ravenow.ui.navigateToChat
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -113,11 +110,14 @@ fun MineAgent() {
|
|||||||
key = { idx -> idx }
|
key = { idx -> idx }
|
||||||
) { idx ->
|
) { idx ->
|
||||||
val agentItem = agentList[idx]
|
val agentItem = agentList[idx]
|
||||||
AgentCard(agentEntity = agentItem,
|
AgentCard(
|
||||||
|
agentEntity = agentItem,
|
||||||
onClick = {
|
onClick = {
|
||||||
model.createSingleChat(agentItem.openId)
|
model.createSingleChat(agentItem.openId)
|
||||||
model.goToChatAi(agentItem.openId,navController)
|
model.goToChatAi(agentItem.openId,navController)
|
||||||
})
|
},
|
||||||
|
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载更多指示器
|
// 加载更多指示器
|
||||||
|
|||||||
@@ -228,10 +228,10 @@ fun MomentsList() {
|
|||||||
) {
|
) {
|
||||||
when (it) {
|
when (it) {
|
||||||
0 -> {
|
0 -> {
|
||||||
Dynamic()
|
Explore()
|
||||||
}
|
}
|
||||||
1 -> {
|
1 -> {
|
||||||
Explore()
|
Dynamic()
|
||||||
}
|
}
|
||||||
|
|
||||||
2 -> {
|
2 -> {
|
||||||
|
|||||||
@@ -21,11 +21,86 @@ import com.aiosman.ravenow.ui.composables.MomentCard
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 探索
|
* 动态列表
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun Dynamic() {
|
fun Dynamic() {
|
||||||
val model = DynamicViewModel
|
val model = DynamicViewModel
|
||||||
|
var moments = model.moments
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
) {
|
||||||
|
Box(Modifier.pullRefresh(state)) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
state = listState
|
||||||
|
) {
|
||||||
|
items(
|
||||||
|
moments.size,
|
||||||
|
key = { idx -> idx }
|
||||||
|
) { idx ->
|
||||||
|
val momentItem = moments[idx] ?: return@items
|
||||||
|
MomentCard(momentEntity = momentItem,
|
||||||
|
onAddComment = {
|
||||||
|
scope.launch {
|
||||||
|
model.onAddComment(momentItem.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLikeClick = {
|
||||||
|
scope.launch {
|
||||||
|
if (momentItem.liked) {
|
||||||
|
model.dislikeMoment(momentItem.id)
|
||||||
|
} else {
|
||||||
|
model.likeMoment(momentItem.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFavoriteClick = {
|
||||||
|
scope.launch {
|
||||||
|
if (momentItem.isFavorite) {
|
||||||
|
model.unfavoriteMoment(momentItem.id)
|
||||||
|
} else {
|
||||||
|
model.favoriteMoment(momentItem.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFollowClick = {
|
||||||
|
model.followAction(momentItem)
|
||||||
|
},
|
||||||
|
showFollowButton = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PullRefreshIndicator(model.refreshing, state, Modifier.align(Alignment.TopCenter))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
package com.aiosman.ravenow.ui.index.tabs.moment.tabs.dynamic
|
package com.aiosman.ravenow.ui.index.tabs.moment.tabs.dynamic
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import com.aiosman.ravenow.entity.MomentLoaderExtraArgs
|
import com.aiosman.ravenow.entity.MomentLoaderExtraArgs
|
||||||
import com.aiosman.ravenow.ui.index.tabs.moment.BaseMomentModel
|
import com.aiosman.ravenow.ui.index.tabs.moment.BaseMomentModel
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
|
||||||
|
|
||||||
object DynamicViewModel : ViewModel() {
|
object DynamicViewModel : BaseMomentModel() {
|
||||||
|
init {
|
||||||
|
EventBus.getDefault().register(this)
|
||||||
|
|
||||||
|
}
|
||||||
|
override fun extraArgs(): MomentLoaderExtraArgs {
|
||||||
|
return MomentLoaderExtraArgs(explore = true)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,106 +1,790 @@
|
|||||||
package com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre
|
package com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
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.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||||
import androidx.compose.material.pullrefresh.pullRefresh
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.derivedStateOf
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import com.aiosman.ravenow.ui.composables.MomentCard
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.aiosman.ravenow.AppStore
|
||||||
|
import com.aiosman.ravenow.LocalAppTheme
|
||||||
|
import com.aiosman.ravenow.R
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import com.aiosman.ravenow.data.Room
|
||||||
|
import com.aiosman.ravenow.data.Agent
|
||||||
|
import com.aiosman.ravenow.data.api.ApiClient
|
||||||
|
import com.aiosman.ravenow.ui.composables.AgentCard
|
||||||
|
|
||||||
|
// Banner数据类
|
||||||
|
data class BannerItem(
|
||||||
|
val id: Int,
|
||||||
|
val title: String,
|
||||||
|
val subtitle: String,
|
||||||
|
val imageUrl: String,
|
||||||
|
val backgroundImageUrl: String,
|
||||||
|
val userCount: Int,
|
||||||
|
val agentName: String
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun fromRoom(room: Room): BannerItem {
|
||||||
|
return BannerItem(
|
||||||
|
id = room.id,
|
||||||
|
title = room.name ?: "聊天室",
|
||||||
|
subtitle = room.description ?: "欢迎加入我们的聊天室",
|
||||||
|
imageUrl = room.avatar ?: "",
|
||||||
|
backgroundImageUrl = "${ApiClient.BASE_API_URL+"/outside"}${room.recommendBanner}"+"?token="+"${AppStore.token}" ?: "",
|
||||||
|
userCount = room.userCount,
|
||||||
|
agentName = room.creator.profile.nickname
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agent数据类
|
||||||
|
data class AgentItem(
|
||||||
|
val id: Int,
|
||||||
|
val title: String,
|
||||||
|
val desc: String,
|
||||||
|
val avatar: String,
|
||||||
|
val useCount: Int,
|
||||||
|
val author: String
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun fromAgent(agent: Agent): AgentItem {
|
||||||
|
return AgentItem(
|
||||||
|
id = agent.id,
|
||||||
|
title = agent.title,
|
||||||
|
desc = agent.desc,
|
||||||
|
avatar = "${ApiClient.BASE_API_URL+"/outside"}${agent.avatar}"+"?token="+"${AppStore.token}",
|
||||||
|
useCount = agent.useCount,
|
||||||
|
author = agent.author
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 动态列表
|
* 探索页面
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun Explore() {
|
fun Explore() {
|
||||||
val model = ExploreViewModel
|
val AppColors = LocalAppTheme.current
|
||||||
var moments = model.moments
|
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
|
val viewModel: ExploreViewModel = viewModel()
|
||||||
model.refreshPager(
|
|
||||||
pullRefresh = true
|
// 模拟刷新状态
|
||||||
|
var isRefreshing by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
// 监听ViewModel的刷新状态
|
||||||
|
LaunchedEffect(viewModel.isRefreshing) {
|
||||||
|
isRefreshing = viewModel.isRefreshing
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理刷新
|
||||||
|
LaunchedEffect(isRefreshing) {
|
||||||
|
if (isRefreshing) {
|
||||||
|
viewModel.refreshBannerData()
|
||||||
|
viewModel.refreshAgentData()
|
||||||
|
// 模拟网络请求延迟
|
||||||
|
kotlinx.coroutines.delay(2000)
|
||||||
|
isRefreshing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val pullRefreshState = rememberPullRefreshState(
|
||||||
|
refreshing = isRefreshing,
|
||||||
|
onRefresh = {
|
||||||
|
scope.launch {
|
||||||
|
isRefreshing = true
|
||||||
|
viewModel.refreshBannerData()
|
||||||
|
viewModel.refreshAgentData()
|
||||||
|
// 模拟网络请求延迟
|
||||||
|
kotlinx.coroutines.delay(2000)
|
||||||
|
isRefreshing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
})
|
|
||||||
val listState = rememberLazyListState()
|
|
||||||
|
|
||||||
// observe list scrolling
|
@Composable
|
||||||
val reachedBottom by remember {
|
fun AgentCard2(agentItem: AgentItem) {
|
||||||
derivedStateOf {
|
val AppColors = LocalAppTheme.current
|
||||||
val lastVisibleItem = listState.layoutInfo.visibleItemsInfo.lastOrNull()
|
|
||||||
lastVisibleItem?.index != 0 && lastVisibleItem?.index == listState.layoutInfo.totalItemsCount - 2
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(12.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
// 左侧头像
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(48.dp)
|
||||||
|
.background(Color(0xFFF5F5F5), RoundedCornerShape(24.dp)),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
if (agentItem.avatar.isNotEmpty()) {
|
||||||
|
CustomAsyncImage(
|
||||||
|
imageUrl = agentItem.avatar,
|
||||||
|
contentDescription = "Agent头像",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(48.dp)
|
||||||
|
.clip(RoundedCornerShape(24.dp)),
|
||||||
|
contentScale = androidx.compose.ui.layout.ContentScale.Crop
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.mipmap.rider_pro_agent),
|
||||||
|
contentDescription = "默认头像",
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
colorFilter = ColorFilter.tint(AppColors.secondaryText)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// load more if scrolled to bottom
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
LaunchedEffect(reachedBottom) {
|
|
||||||
if (reachedBottom) {
|
// 中间文字内容
|
||||||
model.loadMore()
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(end = 8.dp)
|
||||||
|
) {
|
||||||
|
// 标题
|
||||||
|
Text(
|
||||||
|
text = agentItem.title,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
|
||||||
|
color = AppColors.text,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
|
||||||
|
// 描述
|
||||||
|
Text(
|
||||||
|
text = agentItem.desc,
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = AppColors.secondaryText,
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右侧聊天按钮
|
||||||
|
androidx.compose.material3.Button(
|
||||||
|
onClick = {
|
||||||
|
// 聊天逻辑
|
||||||
|
},
|
||||||
|
colors = androidx.compose.material3.ButtonDefaults.buttonColors(
|
||||||
|
containerColor = AppColors.main,
|
||||||
|
contentColor = Color.White
|
||||||
|
),
|
||||||
|
shape = RoundedCornerShape(20.dp),
|
||||||
|
modifier = Modifier.size(width = 60.dp, height = 32.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "聊天",
|
||||||
|
fontSize = 12.sp,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
model.refreshPager()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AgentPage(agentItems: List<AgentItem>, page: Int) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 0.dp)
|
||||||
|
) {
|
||||||
|
// 显示3个agent
|
||||||
|
agentItems.forEachIndexed { index, agentItem ->
|
||||||
|
AgentCard2(agentItem = agentItem)
|
||||||
|
if (index < agentItems.size - 1) {
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
@Composable
|
||||||
|
fun AgentViewPagerSection(agentItems: List<AgentItem>) {
|
||||||
|
val AppColors = LocalAppTheme.current
|
||||||
|
|
||||||
|
// 每页显示3个agent
|
||||||
|
val itemsPerPage = 3
|
||||||
|
val totalPages = (agentItems.size + itemsPerPage - 1) / itemsPerPage
|
||||||
|
|
||||||
|
if (totalPages > 0) {
|
||||||
|
val pagerState = rememberPagerState(pageCount = { totalPages })
|
||||||
|
|
||||||
|
Column {
|
||||||
|
// Agent内容
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Box(Modifier.pullRefresh(state)) {
|
HorizontalPager(
|
||||||
LazyColumn(
|
state = pagerState,
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize()
|
||||||
state = listState
|
) { page ->
|
||||||
) {
|
AgentPage(
|
||||||
items(
|
agentItems = agentItems.drop(page * itemsPerPage).take(itemsPerPage),
|
||||||
moments.size,
|
page = page
|
||||||
key = { idx -> idx }
|
|
||||||
) { idx ->
|
|
||||||
val momentItem = moments[idx] ?: return@items
|
|
||||||
MomentCard(momentEntity = momentItem,
|
|
||||||
onAddComment = {
|
|
||||||
scope.launch {
|
|
||||||
model.onAddComment(momentItem.id)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onLikeClick = {
|
|
||||||
scope.launch {
|
|
||||||
if (momentItem.liked) {
|
|
||||||
model.dislikeMoment(momentItem.id)
|
|
||||||
} else {
|
|
||||||
model.likeMoment(momentItem.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onFavoriteClick = {
|
|
||||||
scope.launch {
|
|
||||||
if (momentItem.isFavorite) {
|
|
||||||
model.unfavoriteMoment(momentItem.id)
|
|
||||||
} else {
|
|
||||||
model.favoriteMoment(momentItem.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onFollowClick = {
|
|
||||||
model.followAction(momentItem)
|
|
||||||
},
|
|
||||||
showFollowButton = true
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PullRefreshIndicator(model.refreshing, state, Modifier.align(Alignment.TopCenter))
|
|
||||||
|
// 指示器
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 12.dp),
|
||||||
|
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.Center
|
||||||
|
) {
|
||||||
|
repeat(totalPages) { index ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 4.dp)
|
||||||
|
.size(3.dp)
|
||||||
|
.background(
|
||||||
|
color = if (pagerState.currentPage == index) AppColors.main else AppColors.secondaryText.copy(alpha = 0.3f),
|
||||||
|
shape = androidx.compose.foundation.shape.CircleShape
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HotChatRoomCard(roomItem: BannerItem) {
|
||||||
|
val AppColors = LocalAppTheme.current
|
||||||
|
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(160.dp)
|
||||||
|
.height(80.dp),
|
||||||
|
shape = RoundedCornerShape(12.dp),
|
||||||
|
elevation = androidx.compose.material3.CardDefaults.cardElevation(defaultElevation = 4.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(12.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
// 左侧图标
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(32.dp)
|
||||||
|
.background(
|
||||||
|
color = when (roomItem.id % 5) {
|
||||||
|
0 -> Color(0xFFE3F2FD) // 浅蓝色
|
||||||
|
1 -> Color(0xFFF3E5F5) // 浅紫色
|
||||||
|
2 -> Color(0xFFFFF3E0) // 浅橙色
|
||||||
|
3 -> Color(0xFFFCE4EC) // 浅粉色
|
||||||
|
else -> Color(0xFFFFF8E1) // 浅黄色
|
||||||
|
},
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.drawable.rider_pro_group_chat),
|
||||||
|
contentDescription = "chat",
|
||||||
|
modifier = Modifier.size(16.dp),
|
||||||
|
colorFilter = ColorFilter.tint(Color.White)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
|
||||||
|
// 右侧文字
|
||||||
|
Text(
|
||||||
|
text = roomItem.title,
|
||||||
|
fontSize = 12.sp,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W500,
|
||||||
|
color = AppColors.text,
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HotChatRoomSection(roomItems: List<BannerItem>) {
|
||||||
|
LazyRow(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
|
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy(12.dp)
|
||||||
|
) {
|
||||||
|
items(roomItems.size) { index ->
|
||||||
|
HotChatRoomCard(roomItem = roomItems[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BannerCard(bannerItem: BannerItem) {
|
||||||
|
val AppColors = LocalAppTheme.current
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 0.dp),
|
||||||
|
shape = RoundedCornerShape(20.dp),
|
||||||
|
elevation = androidx.compose.material3.CardDefaults.cardElevation(defaultElevation = 0.dp)
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
// 背景图片
|
||||||
|
if (bannerItem.backgroundImageUrl.isNotEmpty()) {
|
||||||
|
CustomAsyncImage(
|
||||||
|
imageUrl = bannerItem.backgroundImageUrl,
|
||||||
|
contentDescription = "背景图片",
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentScale = androidx.compose.ui.layout.ContentScale.Crop
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// 如果没有背景图片,使用默认渐变背景
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(
|
||||||
|
brush = androidx.compose.ui.graphics.Brush.linearGradient(
|
||||||
|
colors = listOf(
|
||||||
|
Color(0xFF4CAF50),
|
||||||
|
Color(0xFF4CAF50).copy(alpha = 0.8f)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 内容
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(20.dp),
|
||||||
|
verticalArrangement = androidx.compose.foundation.layout.Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
// 顶部:用户数量
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.drawable.rider_pro_nav_profile),
|
||||||
|
contentDescription = "chat",
|
||||||
|
modifier = Modifier.size(16.dp),
|
||||||
|
colorFilter = ColorFilter.tint(Color.White)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
Text(
|
||||||
|
text = "${bannerItem.userCount}人在聊",
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = Color.White,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 底部:标题和描述
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = bannerItem.title,
|
||||||
|
fontSize = 24.sp,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.Bold,
|
||||||
|
color = Color.White
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
Text(
|
||||||
|
text = bannerItem.subtitle,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = Color.White.copy(alpha = 0.9f),
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
// Agent信息
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(32.dp)
|
||||||
|
.background(Color.White.copy(alpha = 0.2f), RoundedCornerShape(16.dp)),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = bannerItem.agentName.firstOrNull().toString(),
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = Color.White,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.Bold
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
Text(
|
||||||
|
text = bannerItem.agentName,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = Color.White,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 进入按钮
|
||||||
|
androidx.compose.material3.Button(
|
||||||
|
onClick = {
|
||||||
|
// 进入房间逻辑
|
||||||
|
},
|
||||||
|
colors = androidx.compose.material3.ButtonDefaults.buttonColors(
|
||||||
|
containerColor = Color.White.copy(alpha = 0.9f),
|
||||||
|
contentColor = Color(0xFF4CAF50)
|
||||||
|
),
|
||||||
|
shape = RoundedCornerShape(20.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "进入",
|
||||||
|
fontSize = 14.sp,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W600
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.pullRefresh(pullRefreshState)
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentPadding = androidx.compose.foundation.layout.PaddingValues(16.dp)
|
||||||
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
// 可以添加更多不同高度的内容项
|
||||||
|
// 四个圆角布局
|
||||||
|
item {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 6.dp),
|
||||||
|
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.SpaceEvenly
|
||||||
|
) {
|
||||||
|
// 第一个
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(64.dp)
|
||||||
|
.background(Color(0XFFC686FF), RoundedCornerShape(24.dp)),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.mipmap.rider_pro_group),
|
||||||
|
contentDescription = "创建群聊",
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.size(8.dp))
|
||||||
|
Text(
|
||||||
|
text = "创建群聊",
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = AppColors.text,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二个
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(64.dp)
|
||||||
|
.background(Color(0xFF94f9f2), RoundedCornerShape(24.dp)),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.mipmap.rider_pro_agent),
|
||||||
|
contentDescription = "创建Agent",
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.size(8.dp))
|
||||||
|
Text(
|
||||||
|
text = "创建Agent",
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = AppColors.text,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第三个
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(64.dp)
|
||||||
|
.background(Color(0xFFfafd5d), RoundedCornerShape(24.dp)),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.mipmap.rider_pro_release),
|
||||||
|
contentDescription = "发布动态",
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.size(8.dp))
|
||||||
|
Text(
|
||||||
|
text = "发布动态",
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = AppColors.text,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第四个
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(64.dp)
|
||||||
|
.background(Color(0xFFfc724b), RoundedCornerShape(24.dp)),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.mipmap.rider_pro_fire),
|
||||||
|
contentDescription = "热门Agents",
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.size(8.dp))
|
||||||
|
Text(
|
||||||
|
text = "热门Agents",
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = AppColors.text,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
@Composable
|
||||||
|
fun BannerSection(bannerItems: List<BannerItem>) {
|
||||||
|
val AppColors = LocalAppTheme.current
|
||||||
|
val context = LocalContext.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
val pagerState = rememberPagerState(pageCount = { bannerItems.size })
|
||||||
|
|
||||||
|
Column {
|
||||||
|
// Banner内容
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(362.dp)
|
||||||
|
) {
|
||||||
|
HorizontalPager(
|
||||||
|
state = pagerState,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) { page ->
|
||||||
|
val bannerItem = bannerItems[page]
|
||||||
|
BannerCard(bannerItem = bannerItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 指示器
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 12.dp),
|
||||||
|
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.Center
|
||||||
|
) {
|
||||||
|
bannerItems.forEachIndexed { index, _ ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 4.dp)
|
||||||
|
.size(3.dp)
|
||||||
|
.background(
|
||||||
|
color = if (pagerState.currentPage == index) AppColors.main else AppColors.secondaryText.copy(alpha = 0.3f),
|
||||||
|
shape = androidx.compose.foundation.shape.CircleShape
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 第二块区域:Banner
|
||||||
|
item {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 16.dp)
|
||||||
|
) {
|
||||||
|
// 标题
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.padding(bottom = 12.dp)
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.mipmap.rider_pro_fire2),
|
||||||
|
contentDescription = "fire",
|
||||||
|
modifier = Modifier.size(28.dp),
|
||||||
|
colorFilter = ColorFilter.tint(Color(0xFFEF4444))
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
Text(
|
||||||
|
text = "正在高能对话中",
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
|
||||||
|
color = AppColors.text
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Banner
|
||||||
|
BannerSection(bannerItems = viewModel.bannerItems)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第三块区域:推荐Agent
|
||||||
|
item {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 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))
|
||||||
|
Text(
|
||||||
|
text = "推荐Agent",
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
|
||||||
|
color = AppColors.text
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agent ViewPager
|
||||||
|
AgentViewPagerSection(agentItems = viewModel.agentItems)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第四块区域:热门聊天室
|
||||||
|
item {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 16.dp)
|
||||||
|
) {
|
||||||
|
// 标题
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.padding(bottom = 12.dp)
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.mipmap.rider_pro_group),
|
||||||
|
contentDescription = "chat room",
|
||||||
|
modifier = Modifier.size(16.dp),
|
||||||
|
colorFilter = ColorFilter.tint(Color(0xFFFFA500))
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
Text(
|
||||||
|
text = "热门聊天室",
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
|
||||||
|
color = AppColors.text
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 热门聊天室列表
|
||||||
|
//HotChatRoomSection(roomItems = viewModel.bannerItems)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PullRefreshIndicator(
|
||||||
|
refreshing = isRefreshing,
|
||||||
|
state = pullRefreshState,
|
||||||
|
modifier = Modifier.align(Alignment.TopCenter)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,126 @@
|
|||||||
package com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre
|
package com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre
|
||||||
|
|
||||||
import com.aiosman.ravenow.entity.MomentLoaderExtraArgs
|
import androidx.compose.runtime.getValue
|
||||||
import com.aiosman.ravenow.ui.index.tabs.moment.BaseMomentModel
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import org.greenrobot.eventbus.EventBus
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.aiosman.ravenow.data.Room
|
||||||
|
import com.aiosman.ravenow.data.Agent
|
||||||
|
import com.aiosman.ravenow.data.api.ApiClient
|
||||||
|
import com.aiosman.ravenow.data.api.RaveNowAPI
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class ExploreViewModel : ViewModel() {
|
||||||
|
|
||||||
|
private val apiClient: RaveNowAPI = ApiClient.api
|
||||||
|
|
||||||
|
var bannerItems by mutableStateOf<List<BannerItem>>(emptyList())
|
||||||
|
private set
|
||||||
|
|
||||||
|
var agentItems by mutableStateOf<List<AgentItem>>(emptyList())
|
||||||
|
private set
|
||||||
|
|
||||||
|
var isLoading by mutableStateOf(false)
|
||||||
|
private set
|
||||||
|
|
||||||
|
var isRefreshing by mutableStateOf(false)
|
||||||
|
private set
|
||||||
|
|
||||||
|
var errorMessage by mutableStateOf<String?>(null)
|
||||||
|
private set
|
||||||
|
|
||||||
object ExploreViewModel : BaseMomentModel() {
|
|
||||||
init {
|
init {
|
||||||
EventBus.getDefault().register(this)
|
loadBannerData()
|
||||||
|
loadAgentData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refreshBannerData() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
isRefreshing = true
|
||||||
|
errorMessage = null
|
||||||
|
try {
|
||||||
|
val response = apiClient.getRooms(page = 1, pageSize = 12, isRecommended = 1)
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
val rooms = response.body()?.list ?: emptyList()
|
||||||
|
bannerItems = rooms.map { room ->
|
||||||
|
BannerItem.fromRoom(room)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errorMessage = "获取数据失败: ${response.code()}"
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
errorMessage = "网络请求失败: ${e.message}"
|
||||||
|
} finally {
|
||||||
|
isRefreshing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refreshAgentData() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
isRefreshing = true
|
||||||
|
errorMessage = null
|
||||||
|
try {
|
||||||
|
val response = apiClient.getAgent(page = 1, pageSize = 20, withWorkflow = 1)
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
val agents = response.body()?.data?.list ?: emptyList()
|
||||||
|
agentItems = agents.map { agent ->
|
||||||
|
AgentItem.fromAgent(agent)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errorMessage = "获取Agent数据失败: ${response.code()}"
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
errorMessage = "网络请求失败: ${e.message}"
|
||||||
|
} finally {
|
||||||
|
isRefreshing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadBannerData() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
isLoading = true
|
||||||
|
errorMessage = null
|
||||||
|
try {
|
||||||
|
val response = apiClient.getRooms(page = 1, pageSize = 10, isRecommended = 1)
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
val rooms = response.body()?.list ?: emptyList()
|
||||||
|
bannerItems = rooms.map { room ->
|
||||||
|
BannerItem.fromRoom(room)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errorMessage = "获取数据失败: ${response.code()}"
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
errorMessage = "网络请求失败: ${e.message}"
|
||||||
|
} finally {
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadAgentData() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
isLoading = true
|
||||||
|
errorMessage = null
|
||||||
|
try {
|
||||||
|
val response = apiClient.getAgent(page = 1, pageSize = 20, withWorkflow = 1)
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
val agents = response.body()?.data?.list ?: emptyList()
|
||||||
|
agentItems = agents.map { agent ->
|
||||||
|
AgentItem.fromAgent(agent)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errorMessage = "获取Agent数据失败: ${response.code()}"
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
errorMessage = "网络请求失败: ${e.message}"
|
||||||
|
} finally {
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
override fun extraArgs(): MomentLoaderExtraArgs {
|
|
||||||
return MomentLoaderExtraArgs(explore = true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
23
app/src/main/res/drawable/rider_pro_group_chat.xml
Normal file
23
app/src/main/res/drawable/rider_pro_group_chat.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#110C13"
|
||||||
|
android:strokeWidth="1.432"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:pathData="M8.804 5.458a3.58 3.58 0 0 1 3.074-1.046l5.454 0.797 c2.008 0.293 3.407 2.224 3.125 4.312l-1.022 7.56-1.167-0.17" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#110C13"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M6.494 14.375 7.348-0.04-0.01 1.907-7.349 0.04 z" />
|
||||||
|
<path
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#110C13"
|
||||||
|
android:strokeWidth="1.432"
|
||||||
|
android:pathData="M6.77 8.113 5.171-0.756a3.818 3.818 0 0 1 4.335 3.266l0.51 3.775a3.818 3.818 0 0 1-3.23 4.289L4.565 20 3.54 12.402a3.818 3.818 0 0 1 3.23-4.29z" />
|
||||||
|
</vector>
|
||||||
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_agent.png
Normal file
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_agent.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 636 B |
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_agent2.png
Normal file
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_agent2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 608 B |
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_fire.png
Normal file
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_fire.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 826 B |
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_fire2.png
Normal file
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_fire2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 440 B |
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_group.png
Normal file
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_group.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 816 B |
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_release.png
Normal file
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_release.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 627 B |
Reference in New Issue
Block a user