Merge pull request #90 from Kevinlinpr/zhong_1

优化代码
This commit is contained in:
2025-11-26 10:18:12 +08:00
committed by GitHub
9 changed files with 315 additions and 229 deletions

View File

@@ -54,6 +54,7 @@ import com.airbnb.lottie.compose.LottieAnimation
import com.airbnb.lottie.compose.LottieCompositionSpec
import com.airbnb.lottie.compose.LottieConstants
import com.airbnb.lottie.compose.rememberLottieComposition
import com.aiosman.ravenow.ui.index.tabs.profile.composable.ChatEmptyStateView
/**
* 智能体聊天列表页面
@@ -104,22 +105,7 @@ fun AgentChatListScreen() {
val isNetworkAvailable = NetworkUtils.isNetworkAvailable(context)
if (isNetworkAvailable) {
Image(
painter = painterResource(id = R.mipmap.invalid_name_3),
contentDescription = "null data",
modifier = Modifier.size(120.dp)
)
Spacer(modifier = Modifier.height(12.dp))
Text(
text = stringResource(R.string.no_one_knocked_yet),
color = AppColors.text,
fontSize = 16.sp,
fontWeight = FontWeight.W600,
textAlign = TextAlign.Center,
modifier = Modifier.padding(horizontal = 24.dp),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
ChatEmptyStateView()
} else {
NetworkErrorContentCompact(
onReload = {

View File

@@ -46,6 +46,7 @@ import com.aiosman.ravenow.ui.network.NetworkErrorContentCompact
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.ui.text.font.FontFamily
import com.aiosman.ravenow.ui.index.tabs.profile.composable.ChatEmptyStateView
data class CombinedConversation(
val type: String, // "agent", "group", or "friend"
@@ -230,22 +231,7 @@ fun AllChatListScreen() {
val isNetworkAvailable = NetworkUtils.isNetworkAvailable(context)
if (isNetworkAvailable) {
Image(
painter = painterResource(id = R.mipmap.invalid_name_3),
contentDescription = "null data",
modifier = Modifier.size(120.dp)
)
Spacer(modifier = Modifier.height(12.dp))
Text(
text = stringResource(R.string.no_one_knocked_yet),
color = AppColors.text,
fontSize = 16.sp,
fontWeight = FontWeight.W600,
textAlign = TextAlign.Center,
modifier = Modifier.padding(horizontal = 24.dp),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
ChatEmptyStateView()
} else {
NetworkErrorContentCompact(
onReload = {

View File

@@ -41,6 +41,7 @@ import androidx.compose.ui.graphics.Brush
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.foundation.layout.PaddingValues
import com.aiosman.ravenow.ui.index.tabs.profile.composable.ChatEmptyStateView
@OptIn(ExperimentalMaterialApi::class)
@Composable
@@ -85,22 +86,7 @@ fun FriendChatListScreen() {
val isNetworkAvailable = NetworkUtils.isNetworkAvailable(context)
if (isNetworkAvailable) {
Image(
painter = painterResource(id = R.mipmap.invalid_name_3),
contentDescription = "null data",
modifier = Modifier.size(120.dp)
)
Spacer(modifier = Modifier.height(12.dp))
Text(
text = stringResource(R.string.no_one_knocked_yet),
color = AppColors.text,
fontSize = 16.sp,
fontWeight = FontWeight.W600,
textAlign = TextAlign.Center,
modifier = Modifier.padding(horizontal = 24.dp),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
ChatEmptyStateView()
} else {
NetworkErrorContentCompact(
onReload = {
@@ -279,43 +265,4 @@ fun FriendChatItem(
}
}
}
@Composable
fun ReloadButton(
onClick: () -> Unit
) {
val gradientBrush = Brush.linearGradient(
colors = listOf(
Color(0xFF7c45ed),
Color(0xFF7c68ef),
Color(0xFF7bd8f8)
)
)
Button(
onClick = onClick,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 120.dp)
.height(48.dp),
shape = RoundedCornerShape(30.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Transparent
),
contentPadding = PaddingValues(0.dp)
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(gradientBrush),
contentAlignment = Alignment.Center
) {
Text(
text = stringResource(R.string.Reload),
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = Color.White,
textAlign = TextAlign.Center
)
}
}
}

View File

@@ -36,6 +36,7 @@ import com.aiosman.ravenow.ui.composables.rememberDebouncer
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import com.aiosman.ravenow.utils.NetworkUtils
import com.aiosman.ravenow.ui.network.NetworkErrorContentCompact
import com.aiosman.ravenow.ui.index.tabs.profile.composable.ChatEmptyStateView
@OptIn(ExperimentalMaterialApi::class)
@Composable
@@ -80,22 +81,7 @@ fun GroupChatListScreen() {
val isNetworkAvailable = NetworkUtils.isNetworkAvailable(context)
if (isNetworkAvailable) {
Image(
painter = painterResource(id = R.mipmap.invalid_name_3),
contentDescription = "null data",
modifier = Modifier.size(120.dp)
)
Spacer(modifier = Modifier.height(12.dp))
Text(
text = stringResource(R.string.no_one_knocked_yet),
color = AppColors.text,
fontSize = 16.sp,
fontWeight = FontWeight.W600,
textAlign = TextAlign.Center,
modifier = Modifier.padding(horizontal = 24.dp),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
ChatEmptyStateView()
} else {
NetworkErrorContentCompact(
onReload = {

View File

@@ -0,0 +1,102 @@
package com.aiosman.ravenow.ui.index.tabs.profile.composable
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
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.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
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.R
/**
* 空状态缺省图组件
* 用于显示我的-动态、智能体、群聊无内容时的缺省图片和提示文本
*/
@Composable
fun EmptyStateView(
modifier: Modifier = Modifier,
contentDescription: String = "暂无内容",
text: String = stringResource(R.string.cosmos_awaits),
fontWeight: FontWeight = FontWeight.W600
) {
val AppColors = LocalAppTheme.current
Column(
modifier = modifier
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(id = R.mipmap.l_empty_img),
contentDescription = contentDescription,
modifier = Modifier
.size(width = 181.dp, height = 153.dp),
contentScale = ContentScale.Fit
)
Spacer(modifier = Modifier.height(9.dp))
Text(
text = text,
fontSize = 16.sp,
color = AppColors.text,
fontWeight = fontWeight,
textAlign = TextAlign.Center,
modifier = Modifier.padding(horizontal = 24.dp),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
}
/**
* 消息页统一空状态
*/
@Composable
fun ChatEmptyStateView(
modifier: Modifier = Modifier,
contentDescription: String = "暂无会话",
text: String = stringResource(R.string.no_one_knocked_yet)
) {
val AppColors = LocalAppTheme.current
Column(
modifier = modifier
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(id = R.mipmap.invalid_name_3),
contentDescription = contentDescription,
modifier = Modifier.size(120.dp)
)
Spacer(modifier = Modifier.height(12.dp))
Text(
text = text,
color = AppColors.text,
fontSize = 16.sp,
fontWeight = FontWeight.W600,
textAlign = TextAlign.Center,
modifier = Modifier.padding(horizontal = 24.dp),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
}

View File

@@ -158,7 +158,8 @@ fun GalleryGrid(
modifier = baseModifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(vertical = 60.dp),
.padding(vertical = 60.dp)
.padding(horizontal = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
@@ -201,24 +202,8 @@ fun GalleryGrid(
.padding(vertical = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(id = R.mipmap.l_empty_img),
contentDescription = "暂无图片",
modifier = Modifier
.size(width = 181.dp, height = 153.dp),
)
Spacer(modifier = Modifier.height(9.dp))
Text(
text = stringResource(R.string.cosmos_awaits),
fontSize = 16.sp,
color = AppColors.text,
fontWeight = FontWeight.W600,
textAlign = TextAlign.Center,
modifier = Modifier.padding(horizontal = 24.dp),
maxLines = 2,
overflow = TextOverflow.Ellipsis
EmptyStateView(
contentDescription = "暂无图片"
)
}
} else {

View File

@@ -16,15 +16,16 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.draw.alpha
import com.airbnb.lottie.compose.LottieAnimation
import com.airbnb.lottie.compose.LottieCompositionSpec
import com.airbnb.lottie.compose.LottieConstants
@@ -63,6 +64,8 @@ import com.aiosman.ravenow.ui.navigateToGroupChat
import com.aiosman.ravenow.AppStore
import com.aiosman.ravenow.data.api.ApiClient
import android.util.Base64
import com.aiosman.ravenow.ui.network.ReloadButton
import com.aiosman.ravenow.utils.NetworkUtils.isNetworkAvailable
@OptIn(ExperimentalMaterialApi::class)
@Composable
@@ -78,6 +81,7 @@ fun GroupChatEmptyContent(
val context = LocalContext.current
val navController = LocalNavController.current
val viewModel = MyProfileViewModel
val networkAvailable = isNetworkAvailable(context)
// 如果查看其他用户的房间固定使用全部类型filterType = 0
val filterType = if (showSegments) selectedSegment else 0
@@ -133,48 +137,52 @@ fun GroupChatEmptyContent(
horizontalAlignment = Alignment.CenterHorizontally
) {
// 空状态插图
EmptyStateIllustration()
EmptyStateIllustration(
isNetworkAvailable = networkAvailable,
onReload = {
MyProfileViewModel.ResetModel()
MyProfileViewModel.loadProfile(pullRefresh = true)
}
)
Spacer(modifier = Modifier.height(9.dp))
// 空状态文本
Text(
text = stringResource(R.string.cosmos_awaits),
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
color = AppColors.text,
textAlign = TextAlign.Center,
modifier = Modifier.padding(horizontal = 24.dp),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
} else {
LazyColumn(
state = listState,
modifier = nestedScrollModifier.fillMaxSize()
) {
itemsIndexed(
items = viewModel.rooms,
key = { _, item -> item.id }
) { index, room ->
RoomItem(
room = room,
onRoomClick = { roomEntity ->
// 导航到群聊聊天界面
navController.navigateToGroupChat(
id = roomEntity.trtcRoomId,
name = roomEntity.name,
avatar = roomEntity.avatar
// 网格布局每行显示2个房间卡片
items(
items = viewModel.rooms.chunked(2),
key = { rowRooms -> rowRooms.firstOrNull()?.id?.toString() ?: "" }
) { rowRooms ->
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 12.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
rowRooms.forEach { room ->
RoomCard(
room = room,
onRoomClick = { roomEntity ->
// 导航到群聊聊天界面
navController.navigateToGroupChat(
id = roomEntity.trtcRoomId,
name = roomEntity.name,
avatar = roomEntity.avatar
)
},
modifier = Modifier.weight(1f)
)
}
)
if (index < viewModel.rooms.size - 1) {
HorizontalDivider(
modifier = Modifier.padding(horizontal = 24.dp),
color = AppColors.divider
)
// 如果这一行只有一个房间,添加一个空的占位符
if (rowRooms.size == 1) {
Spacer(modifier = Modifier.weight(1f))
}
}
}
@@ -216,6 +224,120 @@ fun GroupChatEmptyContent(
}
}
@Composable
fun RoomCard(
room: RoomEntity,
onRoomClick: (RoomEntity) -> Unit = {},
modifier: Modifier = Modifier
) {
val AppColors = LocalAppTheme.current
val context = LocalContext.current
val roomDebouncer = rememberDebouncer()
val cardSize = 180.dp
// 构建头像URL
val avatarUrl = if (room.avatar.isNotEmpty()) {
"${ConstVars.BASE_SERVER}/api/v1/outside/${room.avatar}?token=${AppStore.token}"
} else {
// 如果头像为空,使用群头像接口
val groupIdBase64 = Base64.encodeToString(
room.trtcType.toByteArray(),
Base64.NO_WRAP
)
"${ApiClient.RETROFIT_URL}group/avatar?groupIdBase64=${groupIdBase64}&token=${AppStore.token}"
}
// 优先显示cover如果没有cover则显示recommendBanner最后显示avatar
val imageUrl = when {
room.cover.isNotEmpty() -> "${ConstVars.BASE_SERVER}/api/v1/outside/${room.cover}?token=${AppStore.token}"
room.recommendBanner.isNotEmpty() -> "${ConstVars.BASE_SERVER}/api/v1/outside/${room.recommendBanner}?token=${AppStore.token}"
else -> avatarUrl
}
// 正方形卡片,文字重叠在底部
Box(
modifier = modifier
.size(cardSize)
.background(AppColors.tabUnselectedBackground, RoundedCornerShape(12.dp))
.noRippleClickable {
roomDebouncer {
onRoomClick(room)
}
}
) {
CustomAsyncImage(
context = context,
imageUrl = imageUrl,
contentDescription = room.name,
modifier = Modifier
.width(cardSize)
.height(120.dp)
.clip(RoundedCornerShape(
topStart = 12.dp,
topEnd = 12.dp,
bottomStart = 0.dp,
bottomEnd = 0.dp)),
contentScale = ContentScale.Crop,
defaultRes = R.mipmap.rider_pro_agent
)
// 房间名称,重叠在底部
Box(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
.padding(bottom = 32.dp, start = 10.dp, end = 10.dp)
.clip(RoundedCornerShape(12.dp))
) {
Text(
text = room.name,
fontSize = 14.sp,
fontWeight = FontWeight.W900,
color = AppColors.text,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Left
)
}
// 显示人数
Box(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
.padding(bottom = 10.dp, start = 10.dp, end = 10.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Image(
painter = painterResource(R.drawable.rider_pro_nav_profile),
contentDescription = "chat",
modifier = Modifier.size(16.dp),
colorFilter = ColorFilter.tint(AppColors.secondaryText)
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = "${room.userCount} ${stringResource(R.string.chatting_now)}",
fontSize = 12.sp,
modifier = Modifier
.alpha(0.6f)
.weight(1f),
color = AppColors.text,
fontWeight = FontWeight.W500,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
}
}
/**
* 列表样式的房间项,供搜索等场景复用
*/
@Composable
fun RoomItem(
room: RoomEntity,
@@ -229,7 +351,6 @@ fun RoomItem(
val avatarUrl = if (room.avatar.isNotEmpty()) {
"${ConstVars.BASE_SERVER}/api/v1/outside/${room.avatar}?token=${AppStore.token}"
} else {
// 如果头像为空,使用群头像接口
val groupIdBase64 = Base64.encodeToString(
room.trtcType.toByteArray(),
Base64.NO_WRAP
@@ -388,14 +509,43 @@ private fun SegmentButton(
}
@Composable
private fun EmptyStateIllustration() {
Image(
painter = painterResource(id = R.mipmap.l_empty_img),
contentDescription = "空状态",
modifier = Modifier
.width(181.dp)
.height(153.dp),
contentScale = ContentScale.Fit
)
private fun EmptyStateIllustration(
isNetworkAvailable: Boolean,
onReload: () -> Unit
) {
val AppColors = LocalAppTheme.current
if (isNetworkAvailable) {
EmptyStateView(
contentDescription = "空状态",
fontWeight = FontWeight.SemiBold
)
} else {
Image(
painter = painterResource(id = R.mipmap.invalid_name_10),
contentDescription = "network error",
modifier = Modifier.size(181.dp),
)
Spacer(modifier = Modifier.height(24.dp))
Text(
text = stringResource(R.string.friend_chat_no_network_title),
fontSize = 16.sp,
color = AppColors.text,
fontWeight = FontWeight.W600
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(R.string.friend_chat_no_network_subtitle),
fontSize = 14.sp,
color = AppColors.secondaryText,
fontWeight = FontWeight.W400
)
Spacer(modifier = Modifier.height(16.dp))
ReloadButton(onClick = onReload)
}
}

View File

@@ -281,25 +281,8 @@ fun AgentEmptyContentWithSegments(
horizontalAlignment = Alignment.CenterHorizontally
) {
if (isNetworkAvailable) {
Image(
painter = painterResource(id = R.mipmap.l_empty_img),
contentDescription = "暂无Agent",
modifier = Modifier
.size(width = 181.dp, height = 153.dp)
.align(Alignment.CenterHorizontally),
)
Spacer(modifier = Modifier.height(9.dp))
Text(
text = stringResource(R.string.cosmos_awaits),
fontSize = 16.sp,
color = AppColors.text,
fontWeight = FontWeight.W600,
textAlign = TextAlign.Center,
modifier = Modifier.padding(horizontal = 24.dp),
maxLines = 2,
overflow = TextOverflow.Ellipsis
EmptyStateView(
contentDescription = "暂无Agent"
)
} else {
Image(

View File

@@ -786,42 +786,3 @@ fun SearchPlaceholderContent(
}
}
@Composable
fun ReloadButton(
onClick: () -> Unit
) {
val gradientBrush = Brush.linearGradient(
colors = listOf(
Color(0xFF7c45ed),
Color(0xFF7c68ef),
Color(0xFF7bd8f8)
)
)
Button(
onClick = onClick,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 120.dp)
.height(48.dp),
shape = RoundedCornerShape(30.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Transparent
),
contentPadding = PaddingValues(0.dp)
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(gradientBrush),
contentAlignment = Alignment.Center
) {
Text(
text = stringResource(R.string.Reload),
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = Color.White,
)
}
}
}