UI调整,群聊开发
This commit is contained in:
@@ -269,79 +269,68 @@ fun IndexScreen() {
|
||||
targetValue = if (isSelected) AppColors.brandColorsColor else AppColors.text,
|
||||
animationSpec = tween(durationMillis = 250), label = ""
|
||||
)
|
||||
|
||||
NavigationBarItem(
|
||||
modifier = Modifier.padding(top = 2.dp),
|
||||
selected = isSelected,
|
||||
onClick = {
|
||||
if (it.route === NavigationItem.Add.route) {
|
||||
NewPostViewModel.asNewPost()
|
||||
navController.navigate(NavigationRoute.NewPost.route)
|
||||
return@NavigationBarItem
|
||||
}
|
||||
coroutineScope.launch {
|
||||
pagerState.scrollToPage(idx)
|
||||
}
|
||||
model.tabIndex = idx
|
||||
},
|
||||
colors = NavigationBarItemColors(
|
||||
selectedIconColor = Color.Transparent,
|
||||
selectedTextColor = Color.Transparent,
|
||||
selectedIndicatorColor = Color.Transparent,
|
||||
unselectedIconColor = Color.Transparent,
|
||||
unselectedTextColor = Color.Transparent,
|
||||
disabledIconColor = Color.Transparent,
|
||||
disabledTextColor = Color.Transparent
|
||||
),
|
||||
icon = {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
if (it.route == NavigationItem.Add.route) {
|
||||
// Add按钮:只显示大图标
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(top = 2.dp)
|
||||
.noRippleClickable {
|
||||
if (it.route === NavigationItem.Add.route) {
|
||||
NewPostViewModel.asNewPost()
|
||||
navController.navigate(NavigationRoute.NewPost.route)
|
||||
return@noRippleClickable
|
||||
}
|
||||
coroutineScope.launch {
|
||||
pagerState.scrollToPage(idx)
|
||||
}
|
||||
model.tabIndex = idx
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
if (it.route == NavigationItem.Add.route) {
|
||||
// Add按钮:只显示大图标
|
||||
Image(
|
||||
painter = painterResource(if (isSelected) it.selectedIcon() else it.icon()),
|
||||
contentDescription = it.label(),
|
||||
modifier = Modifier.size(32.dp),
|
||||
colorFilter = if (!isSelected) ColorFilter.tint(AppColors.text) else null
|
||||
)
|
||||
} else {
|
||||
// 其他按钮:图标+文字
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(48.dp)
|
||||
.height(32.dp)
|
||||
.background(
|
||||
color = if (isSelected) AppColors.brandColorsColor.copy(alpha = 0.15f) else Color.Transparent,
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(if (isSelected) it.selectedIcon() else it.icon()),
|
||||
contentDescription = it.label(),
|
||||
modifier = Modifier.size(32.dp),
|
||||
modifier = Modifier.size(24.dp),
|
||||
colorFilter = if (!isSelected) ColorFilter.tint(AppColors.text) else null
|
||||
)
|
||||
} else {
|
||||
// 其他按钮:图标+文字
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(48.dp)
|
||||
.height(32.dp)
|
||||
.background(
|
||||
color = if (isSelected) AppColors.brandColorsColor.copy(alpha = 0.15f) else Color.Transparent,
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(if (isSelected) it.selectedIcon() else it.icon()),
|
||||
contentDescription = it.label(),
|
||||
modifier = Modifier.size(24.dp),
|
||||
colorFilter = if (!isSelected) ColorFilter.tint(AppColors.text) else null
|
||||
)
|
||||
}
|
||||
|
||||
// 文字标签,可控制间距
|
||||
Spacer(modifier = Modifier.height(1.dp))
|
||||
|
||||
Text(
|
||||
text = it.label(),
|
||||
fontSize = 10.sp,
|
||||
color = if (isSelected) AppColors.brandColorsColor else AppColors.text,
|
||||
fontWeight = if (isSelected) FontWeight.W600 else FontWeight.Normal
|
||||
)
|
||||
}
|
||||
|
||||
// 文字标签,可控制间距
|
||||
Spacer(modifier = Modifier.height(1.dp))
|
||||
|
||||
Text(
|
||||
text = it.label(),
|
||||
fontSize = 10.sp,
|
||||
color = if (isSelected) AppColors.brandColorsColor else AppColors.text,
|
||||
fontWeight = if (isSelected) FontWeight.W600 else FontWeight.Normal
|
||||
)
|
||||
}
|
||||
},
|
||||
label = {
|
||||
// 不显示默认标签
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,8 @@ fun Agent() {
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.noRippleClickable {
|
||||
//
|
||||
// 设置标志,表示新增智能体后不需要刷新
|
||||
com.aiosman.ravenow.ui.agent.AddAgentViewModel.isFromAddAgent = true
|
||||
navController.navigate(
|
||||
NavigationRoute.AddAgent.route
|
||||
)
|
||||
@@ -168,7 +169,8 @@ fun Agent() {
|
||||
state = pagerState,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
.weight(1f),
|
||||
beyondBoundsPageCount = 1 // 预加载相邻页面,避免切换时重新加载
|
||||
) {
|
||||
when (it) {
|
||||
0 -> {
|
||||
|
||||
@@ -25,9 +25,11 @@ open class BaseAgentModel :ViewModel(){
|
||||
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
|
||||
@@ -46,6 +48,10 @@ open class BaseAgentModel :ViewModel(){
|
||||
}
|
||||
}
|
||||
|
||||
// 添加智能体到列表顶部,避免重新加载
|
||||
fun addAgentToList(agent: AgentEntity) {
|
||||
agentList = listOf(agent) + agentList
|
||||
}
|
||||
|
||||
fun ResetModel() {
|
||||
agentLoader.clear()
|
||||
|
||||
@@ -23,6 +23,8 @@ 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
|
||||
@@ -60,10 +62,23 @@ fun HotAgent() {
|
||||
}
|
||||
}
|
||||
|
||||
// 只在首次加载时刷新,避免从AddAgent返回时重复刷新
|
||||
LaunchedEffect(Unit) {
|
||||
model.refreshPager()
|
||||
if (model.agentList.isEmpty() && !model.isLoading) {
|
||||
model.refreshPager()
|
||||
}
|
||||
}
|
||||
|
||||
val context = LocalContext.current
|
||||
|
||||
// 当智能体列表加载完成后,预加载图片
|
||||
LaunchedEffect(agentList) {
|
||||
if (agentList.isNotEmpty()) {
|
||||
model.preloadImages(context)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -107,7 +122,7 @@ fun HotAgent() {
|
||||
) {
|
||||
items(
|
||||
agentList.size,
|
||||
key = { idx -> idx }
|
||||
key = { idx -> agentList[idx].id } // 使用智能体ID作为key,避免重新创建
|
||||
) { idx ->
|
||||
val agentItem = agentList[idx]
|
||||
AgentCard(
|
||||
|
||||
@@ -22,10 +22,14 @@ object HotAgentViewModel : ViewModel() {
|
||||
var currentPage by mutableStateOf(1)
|
||||
var error by mutableStateOf<String?>(null)
|
||||
|
||||
// 记录已预加载的图片ID,避免重复加载
|
||||
private val preloadedImageIds = mutableSetOf<Int>()
|
||||
|
||||
private val pageSize = 20
|
||||
|
||||
init {
|
||||
refreshPager()
|
||||
// 延迟初始化,避免在页面切换时立即加载
|
||||
// refreshPager()
|
||||
}
|
||||
|
||||
fun refreshPager(pullRefresh: Boolean = false) {
|
||||
@@ -37,6 +41,11 @@ object HotAgentViewModel : ViewModel() {
|
||||
refreshing = pullRefresh
|
||||
error = null
|
||||
|
||||
// 清除预加载记录,强制重新加载图片
|
||||
if (pullRefresh) {
|
||||
clearPreloadedImages()
|
||||
}
|
||||
|
||||
val response = ApiClient.api.getAgent(
|
||||
page = 1,
|
||||
pageSize = pageSize
|
||||
@@ -45,7 +54,17 @@ object HotAgentViewModel : ViewModel() {
|
||||
val body = response.body()
|
||||
if (body != null) {
|
||||
val newAgents = body.data.list.map { it.toAgentEntity() }
|
||||
agentList = newAgents
|
||||
// 只有在列表为空或者是下拉刷新时才替换整个列表
|
||||
if (agentList.isEmpty() || pullRefresh) {
|
||||
agentList = newAgents
|
||||
} else {
|
||||
// 否则只添加新的智能体
|
||||
val existingIds = agentList.map { it.id }.toSet()
|
||||
val newAgentsToAdd = newAgents.filter { it.id !in existingIds }
|
||||
if (newAgentsToAdd.isNotEmpty()) {
|
||||
agentList = agentList + newAgentsToAdd
|
||||
}
|
||||
}
|
||||
currentPage = 1
|
||||
hasNext = newAgents.size == pageSize
|
||||
} else {
|
||||
@@ -108,4 +127,32 @@ object HotAgentViewModel : ViewModel() {
|
||||
createGroup2ChatAi(profile.trtcUserId,"ai_group",navController,profile.id)
|
||||
}
|
||||
}
|
||||
|
||||
// 预加载图片,避免滑动时重复加载
|
||||
fun preloadImages(context: android.content.Context) {
|
||||
viewModelScope.launch {
|
||||
agentList.forEach { agent ->
|
||||
if (agent.id !in preloadedImageIds && agent.avatar.isNotEmpty()) {
|
||||
try {
|
||||
// 预加载头像图片到缓存
|
||||
com.aiosman.ravenow.utils.Utils.getImageLoader(context).enqueue(
|
||||
coil.request.ImageRequest.Builder(context)
|
||||
.data(agent.avatar)
|
||||
.memoryCachePolicy(coil.request.CachePolicy.ENABLED)
|
||||
.diskCachePolicy(coil.request.CachePolicy.ENABLED)
|
||||
.build()
|
||||
)
|
||||
preloadedImageIds.add(agent.id)
|
||||
} catch (e: Exception) {
|
||||
// 忽略预加载错误
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 清除预加载记录(在刷新时调用)
|
||||
fun clearPreloadedImages() {
|
||||
preloadedImageIds.clear()
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ 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
|
||||
@@ -60,10 +61,23 @@ fun MineAgent() {
|
||||
}
|
||||
}
|
||||
|
||||
// 只在首次加载时刷新,避免从AddAgent返回时重复刷新
|
||||
LaunchedEffect(Unit) {
|
||||
model.refreshPager()
|
||||
if (model.agentList.isEmpty() && !model.isLoading) {
|
||||
model.refreshPager()
|
||||
}
|
||||
}
|
||||
|
||||
val context = LocalContext.current
|
||||
|
||||
// 当智能体列表加载完成后,预加载图片
|
||||
LaunchedEffect(agentList) {
|
||||
if (agentList.isNotEmpty()) {
|
||||
model.preloadImages(context)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -107,7 +121,7 @@ fun MineAgent() {
|
||||
) {
|
||||
items(
|
||||
agentList.size,
|
||||
key = { idx -> idx }
|
||||
key = { idx -> agentList[idx].id } // 使用智能体ID作为key,避免重新创建
|
||||
) { idx ->
|
||||
val agentItem = agentList[idx]
|
||||
AgentCard(
|
||||
|
||||
@@ -25,10 +25,14 @@ object MineAgentViewModel : ViewModel() {
|
||||
var currentPage by mutableStateOf(1)
|
||||
var error by mutableStateOf<String?>(null)
|
||||
|
||||
// 记录已预加载的图片ID,避免重复加载
|
||||
private val preloadedImageIds = mutableSetOf<Int>()
|
||||
|
||||
private val pageSize = 20
|
||||
|
||||
init {
|
||||
refreshPager()
|
||||
// 延迟初始化,避免在页面切换时立即加载
|
||||
// refreshPager()
|
||||
}
|
||||
|
||||
fun refreshPager(pullRefresh: Boolean = false) {
|
||||
@@ -40,6 +44,11 @@ object MineAgentViewModel : ViewModel() {
|
||||
refreshing = pullRefresh
|
||||
error = null
|
||||
|
||||
// 清除预加载记录,强制重新加载图片
|
||||
if (pullRefresh) {
|
||||
clearPreloadedImages()
|
||||
}
|
||||
|
||||
val response = ApiClient.api.getMyAgent(
|
||||
page = 1,
|
||||
pageSize = pageSize
|
||||
@@ -48,7 +57,17 @@ object MineAgentViewModel : ViewModel() {
|
||||
val body = response.body()
|
||||
if (body != null) {
|
||||
val newAgents = body.list.map { it.toAgentEntity() }
|
||||
agentList = newAgents
|
||||
// 只有在列表为空或者是下拉刷新时才替换整个列表
|
||||
if (agentList.isEmpty() || pullRefresh) {
|
||||
agentList = newAgents
|
||||
} else {
|
||||
// 否则只添加新的智能体
|
||||
val existingIds = agentList.map { it.id }.toSet()
|
||||
val newAgentsToAdd = newAgents.filter { it.id !in existingIds }
|
||||
if (newAgentsToAdd.isNotEmpty()) {
|
||||
agentList = agentList + newAgentsToAdd
|
||||
}
|
||||
}
|
||||
currentPage = 1
|
||||
hasNext = newAgents.size == pageSize
|
||||
} else {
|
||||
@@ -138,4 +157,37 @@ object MineAgentViewModel : ViewModel() {
|
||||
createGroup2ChatAi(profile.trtcUserId,"ai_group",navController,profile.id)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加新创建的智能体到列表顶部
|
||||
fun addAgentToList(agent: AgentEntity) {
|
||||
agentList = listOf(agent) + agentList
|
||||
}
|
||||
|
||||
// 预加载图片,避免滑动时重复加载
|
||||
fun preloadImages(context: android.content.Context) {
|
||||
viewModelScope.launch {
|
||||
agentList.forEach { agent ->
|
||||
if (agent.id !in preloadedImageIds && agent.avatar.isNotEmpty()) {
|
||||
try {
|
||||
// 预加载头像图片到缓存
|
||||
com.aiosman.ravenow.utils.Utils.getImageLoader(context).enqueue(
|
||||
coil.request.ImageRequest.Builder(context)
|
||||
.data(agent.avatar)
|
||||
.memoryCachePolicy(coil.request.CachePolicy.ENABLED)
|
||||
.diskCachePolicy(coil.request.CachePolicy.ENABLED)
|
||||
.build()
|
||||
)
|
||||
preloadedImageIds.add(agent.id)
|
||||
} catch (e: Exception) {
|
||||
// 忽略预加载错误
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 清除预加载记录(在刷新时调用)
|
||||
fun clearPreloadedImages() {
|
||||
preloadedImageIds.clear()
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,7 @@ fun NotificationsScreen() {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 15.dp, vertical = 8.dp),
|
||||
.padding(horizontal = 15.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(
|
||||
@@ -197,7 +197,7 @@ fun NotificationsScreen() {
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.padding(horizontal = 16.dp),
|
||||
.padding(start = 16.dp,bottom = 16.dp),
|
||||
// center the tabs
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.Bottom
|
||||
@@ -251,43 +251,9 @@ fun NotificationsScreen() {
|
||||
2 -> {
|
||||
FriendChatListScreen()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth()
|
||||
,
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (AppState.enableChat){
|
||||
ChatMessageList(
|
||||
MessageListViewModel.chatList,
|
||||
onUserAvatarClick = { conv ->
|
||||
MessageListViewModel.goToUserDetail(conv, navController)
|
||||
},
|
||||
) { conv ->
|
||||
MessageListViewModel.goToChat(conv, navController)
|
||||
}
|
||||
}else{
|
||||
// center text
|
||||
Text(
|
||||
text = "Chat service is under maintenance",
|
||||
color = AppColors.text,
|
||||
fontSize = 16.sp
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
PullRefreshIndicator(
|
||||
MessageListViewModel.isLoading,
|
||||
state,
|
||||
Modifier.align(Alignment.TopCenter)
|
||||
)*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,131 +326,3 @@ fun NotificationIndicator(
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NotificationCounterItem(count: Int) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
val context = LocalContext.current
|
||||
var clickCount by remember { mutableStateOf(0) }
|
||||
Row(
|
||||
modifier = Modifier.padding(vertical = 16.dp, horizontal = 32.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_notification),
|
||||
contentDescription = "",
|
||||
modifier = Modifier
|
||||
.size(24.dp).noRippleClickable {
|
||||
clickCount++
|
||||
if (clickCount > 5) {
|
||||
clickCount = 0
|
||||
AppStore.saveDarkMode(!AppState.darkMode)
|
||||
Toast.makeText(context, "Dark mode: ${AppState.darkMode},please restart app", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
colorFilter = ColorFilter.tint(AppColors.text)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(24.dp))
|
||||
Text(stringResource(R.string.notifications_upper), fontSize = 18.sp, color = AppColors.text)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
if (count > 0) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(AppColors.main, RoundedCornerShape(16.dp))
|
||||
.padding(horizontal = 8.dp, vertical = 2.dp)
|
||||
) {
|
||||
Text(
|
||||
text = count.toString(),
|
||||
color = AppColors.mainText,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun ChatMessageList(
|
||||
items: List<Conversation>,
|
||||
onUserAvatarClick: (Conversation) -> Unit = {},
|
||||
onChatClick: (Conversation) -> Unit = {}
|
||||
) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
items(items.size) { index ->
|
||||
val item = items[index]
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp)
|
||||
) {
|
||||
Box {
|
||||
CustomAsyncImage(
|
||||
context = LocalContext.current,
|
||||
imageUrl = item.avatar,
|
||||
contentDescription = item.nickname,
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(RoundedCornerShape(48.dp))
|
||||
.noRippleClickable {
|
||||
onUserAvatarClick(item)
|
||||
}
|
||||
)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 12.dp)
|
||||
.noRippleClickable {
|
||||
onChatClick(item)
|
||||
}
|
||||
) {
|
||||
Row {
|
||||
Text(
|
||||
text = item.nickname,
|
||||
fontSize = 16.sp,
|
||||
modifier = Modifier,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = AppColors.text
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text(
|
||||
text = item.lastMessageTime,
|
||||
fontSize = 14.sp,
|
||||
color = AppColors.secondaryText,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
Row {
|
||||
Text(
|
||||
text = "${if (item.isSelf) "Me: " else ""}${item.displayText}",
|
||||
fontSize = 14.sp,
|
||||
maxLines = 1,
|
||||
color = AppColors.secondaryText,
|
||||
modifier = Modifier.weight(1f),
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
if (item.unreadCount > 0) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(AppColors.main, CircleShape)
|
||||
.padding(horizontal = 8.dp, vertical = 2.dp)
|
||||
) {
|
||||
Text(
|
||||
text = item.unreadCount.toString(),
|
||||
color = AppColors.mainText,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
@@ -27,6 +28,7 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
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.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
@@ -113,13 +115,7 @@ fun AgentChatListScreen() {
|
||||
model.goToChatAi(conv.trtcUserId,navController)
|
||||
}
|
||||
)
|
||||
|
||||
if (index < AgentChatListViewModel.agentChatList.size - 1) {
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(horizontal = 24.dp),
|
||||
color = AppColors.divider
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 加载更多指示器
|
||||
@@ -174,16 +170,16 @@ fun AgentChatItem(
|
||||
onChatClick: (AgentConversation) -> Unit = {}
|
||||
) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp, vertical = 12.dp)
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||
.noRippleClickable {
|
||||
onChatClick(conversation)
|
||||
}
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// 头像
|
||||
Box {
|
||||
CustomAsyncImage(
|
||||
context = LocalContext.current,
|
||||
@@ -191,18 +187,18 @@ fun AgentChatItem(
|
||||
contentDescription = conversation.nickname,
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(CircleShape)
|
||||
.clip(RoundedCornerShape(48.dp))
|
||||
.noRippleClickable {
|
||||
onUserAvatarClick(conversation)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 聊天信息
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 12.dp)
|
||||
.padding(start = 12.dp, top = 2.dp),
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -210,53 +206,52 @@ fun AgentChatItem(
|
||||
) {
|
||||
Text(
|
||||
text = conversation.nickname,
|
||||
fontSize = 16.sp,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = AppColors.text,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
|
||||
Spacer(modifier = Modifier.width(6.dp))
|
||||
|
||||
Text(
|
||||
text = conversation.lastMessageTime,
|
||||
fontSize = 12.sp,
|
||||
fontSize = 11.sp,
|
||||
color = AppColors.secondaryText
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = "${if (conversation.isSelf) stringResource(R.string.agent_chat_me_prefix) else ""}${conversation.displayText}",
|
||||
fontSize = 14.sp,
|
||||
text = "${if (conversation.isSelf) stringResource(R.string.friend_chat_me_prefix) else ""}${conversation.displayText}",
|
||||
fontSize = 12.sp,
|
||||
color = AppColors.secondaryText,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
// 未读消息数量
|
||||
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
|
||||
if (conversation.unreadCount > 0) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(if (conversation.unreadCount > 99) 24.dp else 20.dp)
|
||||
.background(
|
||||
color = AppColors.main,
|
||||
color = Color(0xFFFF3B30),
|
||||
shape = CircleShape
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = if (conversation.unreadCount > 99) "99+" else conversation.unreadCount.toString(),
|
||||
color = AppColors.mainText,
|
||||
fontSize = if (conversation.unreadCount > 99) 9.sp else 10.sp,
|
||||
color = Color.White,
|
||||
fontSize = if (conversation.unreadCount > 99) 11.sp else 12.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
@@ -17,6 +18,7 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
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.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -97,12 +99,6 @@ fun FriendChatListScreen() {
|
||||
}
|
||||
)
|
||||
|
||||
if (index < FriendChatListViewModel.friendChatList.size - 1) {
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(horizontal = 24.dp),
|
||||
color = AppColors.divider
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (FriendChatListViewModel.isLoading && FriendChatListViewModel.friendChatList.isNotEmpty()) {
|
||||
@@ -158,10 +154,11 @@ fun FriendChatItem(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp, vertical = 12.dp)
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||
.noRippleClickable {
|
||||
onChatClick(conversation)
|
||||
}
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box {
|
||||
CustomAsyncImage(
|
||||
@@ -170,17 +167,18 @@ fun FriendChatItem(
|
||||
contentDescription = conversation.nickname,
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(CircleShape)
|
||||
.clip(RoundedCornerShape(48.dp))
|
||||
.noRippleClickable {
|
||||
onUserAvatarClick(conversation)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 12.dp)
|
||||
.padding(start = 12.dp, top = 2.dp),
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -188,52 +186,52 @@ fun FriendChatItem(
|
||||
) {
|
||||
Text(
|
||||
text = conversation.nickname,
|
||||
fontSize = 16.sp,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = AppColors.text,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
|
||||
Spacer(modifier = Modifier.width(6.dp))
|
||||
|
||||
Text(
|
||||
text = conversation.lastMessageTime,
|
||||
fontSize = 12.sp,
|
||||
fontSize = 11.sp,
|
||||
color = AppColors.secondaryText
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = "${if (conversation.isSelf) stringResource(R.string.friend_chat_me_prefix) else ""}${conversation.displayText}",
|
||||
fontSize = 14.sp,
|
||||
fontSize = 12.sp,
|
||||
color = AppColors.secondaryText,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
|
||||
if (conversation.unreadCount > 0) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(if (conversation.unreadCount > 99) 24.dp else 20.dp)
|
||||
.background(
|
||||
color = AppColors.main,
|
||||
color = Color(0xFFFF3B30),
|
||||
shape = CircleShape
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = if (conversation.unreadCount > 99) "99+" else conversation.unreadCount.toString(),
|
||||
color = AppColors.mainText,
|
||||
fontSize = if (conversation.unreadCount > 99) 9.sp else 10.sp,
|
||||
color = Color.White,
|
||||
fontSize = if (conversation.unreadCount > 99) 11.sp else 12.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
@@ -13,10 +14,12 @@ import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
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.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -45,7 +48,17 @@ fun GroupChatListScreen() {
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
GroupChatListViewModel.refreshPager(context = context)
|
||||
// 初始化消息监听器
|
||||
GroupChatListViewModel.initMessageListener(context)
|
||||
}
|
||||
|
||||
// 在组件销毁时清理监听器
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
GroupChatListViewModel.removeMessageListener()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -96,12 +109,6 @@ fun GroupChatListScreen() {
|
||||
}
|
||||
)
|
||||
|
||||
if (index < GroupChatListViewModel.groupChatList.size - 1) {
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(horizontal = 24.dp),
|
||||
color = AppColors.divider
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (GroupChatListViewModel.isLoading && GroupChatListViewModel.groupChatList.isNotEmpty()) {
|
||||
@@ -157,10 +164,11 @@ fun GroupChatItem(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp, vertical = 12.dp)
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||
.noRippleClickable {
|
||||
onChatClick(conversation)
|
||||
}
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box {
|
||||
CustomAsyncImage(
|
||||
@@ -169,7 +177,7 @@ fun GroupChatItem(
|
||||
contentDescription = conversation.groupName,
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(CircleShape)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.noRippleClickable {
|
||||
onGroupAvatarClick(conversation)
|
||||
}
|
||||
@@ -179,7 +187,8 @@ fun GroupChatItem(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 12.dp)
|
||||
.padding(start = 12.dp, top = 2.dp),
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -187,22 +196,22 @@ fun GroupChatItem(
|
||||
) {
|
||||
Text(
|
||||
text = conversation.groupName,
|
||||
fontSize = 16.sp,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = AppColors.text,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Spacer(modifier = Modifier.width(6.dp))
|
||||
|
||||
Text(
|
||||
text = conversation.lastMessageTime,
|
||||
fontSize = 12.sp,
|
||||
fontSize = 11.sp,
|
||||
color = AppColors.secondaryText
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -210,30 +219,29 @@ fun GroupChatItem(
|
||||
) {
|
||||
Text(
|
||||
text = "${if (conversation.isSelf) stringResource(R.string.friend_chat_me_prefix) else ""}${conversation.displayText}",
|
||||
fontSize = 14.sp,
|
||||
fontSize = 12.sp,
|
||||
color = AppColors.secondaryText,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
|
||||
if (conversation.unreadCount > 0) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(if (conversation.unreadCount > 99) 24.dp else 20.dp)
|
||||
.background(
|
||||
color = AppColors.main,
|
||||
color = Color(0xFFFF3B30),
|
||||
shape = CircleShape
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = if (conversation.unreadCount > 99) "99+" else conversation.unreadCount.toString(),
|
||||
color = AppColors.mainText,
|
||||
fontSize = if (conversation.unreadCount > 99) 9.sp else 10.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
color = Color.White,
|
||||
fontSize = if (conversation.unreadCount > 99) 11.sp else 12.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,12 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.NavHostController
|
||||
import com.aiosman.ravenow.AppState
|
||||
import com.aiosman.ravenow.AppStore
|
||||
import com.aiosman.ravenow.ConstVars
|
||||
import com.aiosman.ravenow.data.UserService
|
||||
import com.aiosman.ravenow.data.UserServiceImpl
|
||||
import com.aiosman.ravenow.data.api.ApiClient
|
||||
import com.aiosman.ravenow.data.api.GroupChatRequestBody
|
||||
import com.aiosman.ravenow.exp.formatChatTime
|
||||
import com.aiosman.ravenow.ui.NavigationRoute
|
||||
import com.aiosman.ravenow.ui.navigateToChat
|
||||
@@ -22,6 +25,8 @@ import com.tencent.imsdk.v2.V2TIMConversationResult
|
||||
import com.tencent.imsdk.v2.V2TIMManager
|
||||
import com.tencent.imsdk.v2.V2TIMMessage
|
||||
import com.tencent.imsdk.v2.V2TIMValueCallback
|
||||
import com.tencent.imsdk.v2.V2TIMAdvancedMsgListener
|
||||
import com.tencent.imsdk.v2.V2TIMConversationListener
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
@@ -70,7 +75,16 @@ data class GroupConversation(
|
||||
groupName = msg.showName,
|
||||
lastMessage = msg.lastMessage?.textElem?.text ?: "",
|
||||
lastMessageTime = lastMessage.time.formatChatTime(context),
|
||||
avatar = "${ConstVars.BASE_SERVER}${msg.faceUrl}",
|
||||
avatar = if (msg.faceUrl.isNullOrEmpty()) {
|
||||
// 将 groupId 转换为 Base64
|
||||
val groupIdBase64 = android.util.Base64.encodeToString(
|
||||
msg.groupID.toByteArray(),
|
||||
android.util.Base64.NO_WRAP
|
||||
)
|
||||
"${ApiClient.RETROFIT_URL+"group/avatar?groupIdBase64="}${groupIdBase64}"+"&token="+"${AppStore.token}"
|
||||
} else {
|
||||
"${ApiClient.BASE_API_URL+"/outside/rooms/avatar/"}${msg.faceUrl}"+"?token="+"${AppStore.token}"
|
||||
},
|
||||
unreadCount = msg.unreadCount,
|
||||
displayText = displayText,
|
||||
isSelf = msg.lastMessage?.sender == AppState.profile?.trtcUserId,
|
||||
@@ -90,6 +104,10 @@ object GroupChatListViewModel : ViewModel() {
|
||||
var error by mutableStateOf<String?>(null)
|
||||
|
||||
private val pageSize = 20
|
||||
|
||||
// 消息监听器
|
||||
private var messageListener: V2TIMAdvancedMsgListener? = null
|
||||
private var conversationListener: V2TIMConversationListener? = null
|
||||
|
||||
fun refreshPager(pullRefresh: Boolean = false, context: Context? = null) {
|
||||
if (isLoading && !pullRefresh) return
|
||||
@@ -162,14 +180,24 @@ object GroupChatListViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun createGroupChat(
|
||||
trtcGroupId: String,
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val response = ApiClient.api.createGroupChatAi(trtcGroupId = trtcGroupId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun goToChat(
|
||||
conversation: GroupConversation,
|
||||
navController: NavHostController
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
createGroupChat(trtcGroupId = conversation.groupId)
|
||||
// 群聊直接使用群ID进行导航
|
||||
navController.navigateToGroupChat(conversation.groupId)
|
||||
navController.navigateToGroupChat(conversation.groupId, conversation.groupName, conversation.avatar)
|
||||
} catch (e: Exception) {
|
||||
error = ""
|
||||
e.printStackTrace()
|
||||
@@ -184,7 +212,7 @@ object GroupChatListViewModel : ViewModel() {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
// 可以导航到群详情页面,这里暂时使用群聊页面
|
||||
navController.navigateToChat(conversation.groupId)
|
||||
//
|
||||
} catch (e: Exception) {
|
||||
error = ""
|
||||
e.printStackTrace()
|
||||
@@ -197,4 +225,77 @@ object GroupChatListViewModel : ViewModel() {
|
||||
loadGroupChatList(context)
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化消息监听器
|
||||
fun initMessageListener(context: Context) {
|
||||
// 消息监听器 - 监听新消息
|
||||
messageListener = object : V2TIMAdvancedMsgListener() {
|
||||
override fun onRecvNewMessage(msg: V2TIMMessage?) {
|
||||
super.onRecvNewMessage(msg)
|
||||
msg?.let { message ->
|
||||
if (message.groupID != null && message.groupID.isNotEmpty()) {
|
||||
// 收到群聊消息,刷新群聊列表
|
||||
android.util.Log.i("GroupChatList", "收到群聊消息,刷新列表")
|
||||
refreshGroupChatList(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 会话监听器 - 监听会话变化
|
||||
conversationListener = object : V2TIMConversationListener() {
|
||||
override fun onConversationChanged(conversationList: MutableList<V2TIMConversation>?) {
|
||||
super.onConversationChanged(conversationList)
|
||||
// 会话发生变化,刷新群聊列表
|
||||
conversationList?.let { conversations ->
|
||||
val hasGroupConversation = conversations.any { it.type == V2TIMConversation.V2TIM_GROUP }
|
||||
if (hasGroupConversation) {
|
||||
android.util.Log.i("GroupChatList", "群聊会话发生变化,刷新列表")
|
||||
refreshGroupChatList(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewConversation(conversationList: MutableList<V2TIMConversation>?) {
|
||||
super.onNewConversation(conversationList)
|
||||
// 新增会话,刷新群聊列表
|
||||
conversationList?.let { conversations ->
|
||||
val hasGroupConversation = conversations.any { it.type == V2TIMConversation.V2TIM_GROUP }
|
||||
if (hasGroupConversation) {
|
||||
android.util.Log.i("GroupChatList", "新增群聊会话,刷新列表")
|
||||
refreshGroupChatList(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 注册监听器
|
||||
V2TIMManager.getMessageManager().addAdvancedMsgListener(messageListener)
|
||||
V2TIMManager.getConversationManager().addConversationListener(conversationListener)
|
||||
}
|
||||
|
||||
// 移除消息监听器
|
||||
fun removeMessageListener() {
|
||||
messageListener?.let {
|
||||
V2TIMManager.getMessageManager().removeAdvancedMsgListener(it)
|
||||
}
|
||||
conversationListener?.let {
|
||||
V2TIMManager.getConversationManager().removeConversationListener(it)
|
||||
}
|
||||
messageListener = null
|
||||
conversationListener = null
|
||||
}
|
||||
|
||||
// 刷新群聊列表
|
||||
private fun refreshGroupChatList(context: Context) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
loadGroupChatList(context)
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("GroupChatList", "刷新群聊列表失败: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.draw.blur
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.aiosman.ravenow.AppStore
|
||||
@@ -232,9 +233,9 @@ fun Explore() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AgentPage(agentItems: List<AgentItem>, page: Int) {
|
||||
fun AgentPage(agentItems: List<AgentItem>, page: Int, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 0.dp)
|
||||
) {
|
||||
@@ -268,11 +269,27 @@ fun Explore() {
|
||||
) {
|
||||
HorizontalPager(
|
||||
state = pagerState,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = androidx.compose.foundation.layout.PaddingValues(horizontal = 4.dp),
|
||||
pageSpacing = 0.dp
|
||||
) { page ->
|
||||
// 计算当前页面的偏移量
|
||||
val pageOffset = (
|
||||
(pagerState.currentPage - page) + pagerState
|
||||
.currentPageOffsetFraction
|
||||
).coerceIn(-1f, 1f)
|
||||
|
||||
// 根据偏移量计算缩放比例
|
||||
val scale = 1f - (0.1f * kotlin.math.abs(pageOffset))
|
||||
|
||||
AgentPage(
|
||||
agentItems = agentItems.drop(page * itemsPerPage).take(itemsPerPage),
|
||||
page = page
|
||||
page = page,
|
||||
modifier = Modifier
|
||||
.graphicsLayer {
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -410,12 +427,12 @@ fun Explore() {
|
||||
|
||||
|
||||
@Composable
|
||||
fun BannerCard(bannerItem: BannerItem) {
|
||||
fun BannerCard(bannerItem: BannerItem, modifier: Modifier = Modifier) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
val context = LocalContext.current
|
||||
|
||||
Card(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 0.dp),
|
||||
shape = RoundedCornerShape(20.dp),
|
||||
@@ -595,15 +612,15 @@ fun Explore() {
|
||||
) {
|
||||
|
||||
|
||||
// 可以添加更多不同高度的内容项
|
||||
// 第一块区域
|
||||
item {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 6.dp),
|
||||
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.SpaceEvenly
|
||||
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.SpaceBetween
|
||||
) {
|
||||
// 第一个
|
||||
// 第一个 - 靠左显示
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
@@ -633,69 +650,75 @@ fun Explore() {
|
||||
)
|
||||
}
|
||||
|
||||
// 第二个
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
navController.navigate(
|
||||
NavigationRoute.AddAgent.route)
|
||||
},
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
// 中间两个 - 平均分布
|
||||
Row(
|
||||
modifier = Modifier.weight(1f),
|
||||
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.SpaceEvenly
|
||||
) {
|
||||
Box(
|
||||
// 第二个
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.size(64.dp)
|
||||
.background(Color(0xFF94f9f2), RoundedCornerShape(24.dp)),
|
||||
contentAlignment = Alignment.Center
|
||||
.clickable {
|
||||
navController.navigate(
|
||||
NavigationRoute.AddAgent.route)
|
||||
},
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.mipmap.rider_pro_agent),
|
||||
contentDescription = "创建智能体",
|
||||
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(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
NewPostViewModel.asNewPost()
|
||||
navController.navigate("NewPost")
|
||||
},
|
||||
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),
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(64.dp)
|
||||
.background(Color(0xFF94f9f2), RoundedCornerShape(24.dp)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.mipmap.rider_pro_agent),
|
||||
contentDescription = "创建智能体",
|
||||
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(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
NewPostViewModel.asNewPost()
|
||||
navController.navigate("NewPost")
|
||||
},
|
||||
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
|
||||
)
|
||||
}
|
||||
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
|
||||
) {
|
||||
@@ -719,9 +742,9 @@ fun Explore() {
|
||||
color = AppColors.text,
|
||||
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
@@ -767,10 +790,28 @@ fun BannerSection(bannerItems: List<BannerItem>) {
|
||||
) {
|
||||
HorizontalPager(
|
||||
state = pagerState,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = androidx.compose.foundation.layout.PaddingValues(horizontal = 4.dp),
|
||||
) { page ->
|
||||
val bannerItem = bannerItems[page]
|
||||
BannerCard(bannerItem = bannerItem)
|
||||
|
||||
// 计算当前页面的偏移量
|
||||
val pageOffset = (
|
||||
(pagerState.currentPage - page) + pagerState
|
||||
.currentPageOffsetFraction
|
||||
).coerceIn(-1f, 1f)
|
||||
|
||||
// 根据偏移量计算缩放比例
|
||||
val scale = 1f - (0.1f * kotlin.math.abs(pageOffset))
|
||||
|
||||
BannerCard(
|
||||
bannerItem = bannerItem,
|
||||
modifier = Modifier
|
||||
.graphicsLayer {
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user