From 2a7d310be5966de069e6b9025be75d67eb571b4f Mon Sep 17 00:00:00 2001 From: weber Date: Wed, 27 Aug 2025 16:37:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=BD=91=E7=BB=9C=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=EF=BC=8C=E4=BF=AE=E6=94=B9bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 1 + .../ravenow/ui/account/AccountSetting.kt | 3 +- .../com/aiosman/ravenow/ui/agent/AddAgent.kt | 14 +- .../ravenow/ui/agent/AddAgentViewModel.kt | 11 + .../aiosman/ravenow/ui/chat/ChatAiScreen.kt | 192 ++++++++++-------- .../com/aiosman/ravenow/ui/chat/ChatScreen.kt | 80 +++++--- .../ravenow/ui/chat/GroupChatScreen.kt | 16 +- .../ravenow/ui/composables/DropdownMenu.kt | 78 ++++--- .../com/aiosman/ravenow/ui/index/Index.kt | 12 +- .../tabs/message/tab/AgentChatListScreen.kt | 9 +- .../tabs/message/tab/FriendChatListScreen.kt | 7 +- .../tabs/message/tab/GroupChatListScreen.kt | 76 +++---- .../ui/index/tabs/profile/ProfileV3.kt | 38 +++- .../com/aiosman/ravenow/utils/NetworkUtils.kt | 71 +++++++ 14 files changed, 382 insertions(+), 226 deletions(-) create mode 100644 app/src/main/java/com/aiosman/ravenow/utils/NetworkUtils.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4550ef6..169a1af 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools"> + diff --git a/app/src/main/java/com/aiosman/ravenow/ui/account/AccountSetting.kt b/app/src/main/java/com/aiosman/ravenow/ui/account/AccountSetting.kt index a3ee75f..093d2dc 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/account/AccountSetting.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/account/AccountSetting.kt @@ -92,7 +92,8 @@ fun AccountSetting() { ), dragHandle = {}, shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp), - windowInsets = WindowInsets(0) + windowInsets = WindowInsets(0), + modifier = Modifier.height(600.dp) // 设置固定高度 ) { RemoveAccountModal( onRemove = { diff --git a/app/src/main/java/com/aiosman/ravenow/ui/agent/AddAgent.kt b/app/src/main/java/com/aiosman/ravenow/ui/agent/AddAgent.kt index e547b38..4ae22db 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/agent/AddAgent.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/agent/AddAgent.kt @@ -19,6 +19,7 @@ import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -86,6 +87,13 @@ fun AddAgentScreen() { } + // 页面退出时清空数据 + DisposableEffect(Unit) { + onDispose { + model.clearData() + } + } + Column( modifier = Modifier .fillMaxSize() @@ -230,10 +238,8 @@ fun AddAgentScreen() { val result = model.createAgent(context) if (result != null) { println("AddAgent: Agent created successfully, closing page") - // 创建成功,关闭页面 - model.name = "" - model.desc = "" - model.isFromAddAgent = false // 重置标志 + // 创建成功,清空数据并关闭页面 + model.clearData() navController.popBackStack() } } catch (e: Exception) { diff --git a/app/src/main/java/com/aiosman/ravenow/ui/agent/AddAgentViewModel.kt b/app/src/main/java/com/aiosman/ravenow/ui/agent/AddAgentViewModel.kt index 1efc27f..cc606cc 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/agent/AddAgentViewModel.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/agent/AddAgentViewModel.kt @@ -81,4 +81,15 @@ object AddAgentViewModel : ViewModel() { else -> null } } + + /** + * 清空所有页面数据 + */ + fun clearData() { + name = "" + desc = "" + croppedBitmap = null + isUpdating = false + isFromAddAgent = false + } } \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/ravenow/ui/chat/ChatAiScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/chat/ChatAiScreen.kt index bb7d91e..5c0bfbc 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/chat/ChatAiScreen.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/chat/ChatAiScreen.kt @@ -8,6 +8,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.Crossfade import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -49,6 +50,7 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow import androidx.compose.ui.focus.onFocusChanged @@ -81,6 +83,7 @@ import com.aiosman.ravenow.ui.composables.DropdownMenu import com.aiosman.ravenow.ui.composables.MenuItem import com.aiosman.ravenow.ui.composables.StatusBarSpacer import com.aiosman.ravenow.ui.modifiers.noRippleClickable +import com.aiosman.ravenow.utils.NetworkUtils import com.tencent.imsdk.v2.V2TIMMessage import kotlinx.coroutines.launch import java.util.UUID @@ -228,12 +231,16 @@ fun ChatAiScreen(userId: String) { ) { isMenuExpanded = false - viewModel.viewModelScope.launch { - if (viewModel.notificationStrategy == "mute") { - viewModel.updateNotificationStrategy("active") - } else { - viewModel.updateNotificationStrategy("mute") + if (NetworkUtils.isNetworkAvailable(context)) { + viewModel.viewModelScope.launch { + if (viewModel.notificationStrategy == "mute") { + viewModel.updateNotificationStrategy("active") + } else { + viewModel.updateNotificationStrategy("mute") + } } + } else { + android.widget.Toast.makeText(context, "网络连接异常,请检查网络设置", android.widget.Toast.LENGTH_SHORT).show() } } ), @@ -260,7 +267,11 @@ fun ChatAiScreen(userId: String) { ChatAiInput( onSendImage = { it?.let { - viewModel.sendImageMessage(it, context) + if (NetworkUtils.isNetworkAvailable(context)) { + viewModel.sendImageMessage(it, context) + } else { + android.widget.Toast.makeText(context, "网络连接异常,请检查网络设置", android.widget.Toast.LENGTH_SHORT).show() + } } }, ) { @@ -355,19 +366,28 @@ fun ChatAiSelfItem(item: ChatItem) { Column( horizontalAlignment = androidx.compose.ui.Alignment.End, ) { + /* Text( + text = item.nickname, + style = TextStyle( + color = Color.Gray, + fontSize = 12.sp, + ), + modifier = Modifier.padding(bottom = 2.dp) + ) + */ Box( modifier = Modifier .widthIn( min = 20.dp, max = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 250.dp else 150.dp) ) - .clip(RoundedCornerShape(8.dp)) - .background(Color(0xFF000000)) + .clip(RoundedCornerShape(20.dp)) + .background(Color(0xFF6246FF)) .padding( vertical = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 8.dp else 0.dp), horizontal = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 16.dp else 0.dp) ) - .padding(bottom = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 3.dp else 0.dp)) + ) { when (item.messageType) { V2TIMMessage.V2TIM_ELEM_TYPE_TEXT -> { @@ -375,7 +395,7 @@ fun ChatAiSelfItem(item: ChatItem) { text = item.message, style = TextStyle( color = Color.White, - fontSize = 16.sp, + fontSize = 14.sp, ), textAlign = TextAlign.Start ) @@ -391,28 +411,28 @@ fun ChatAiSelfItem(item: ChatItem) { else -> { Text( - text = "Unsupported message type", + text = "不支持的消息类型", style = TextStyle( color = Color.White, - fontSize = 16.sp, + fontSize = 14.sp, ) ) } } } } - Spacer(modifier = Modifier.width(12.dp)) + /*Spacer(modifier = Modifier.width(12.dp)) Box( modifier = Modifier - .size(40.dp) - .clip(RoundedCornerShape(40.dp)) + .size(24.dp) + .clip(RoundedCornerShape(24.dp)) ) { CustomAsyncImage( imageUrl = item.avatar, modifier = Modifier.fillMaxSize(), contentDescription = "avatar" ) - } + }*/ } } } @@ -432,8 +452,8 @@ fun ChatAiOtherItem(item: ChatItem) { ) { Box( modifier = Modifier - .size(40.dp) - .clip(RoundedCornerShape(40.dp)) + .size(24.dp) + .clip(RoundedCornerShape(24.dp)) ) { CustomAsyncImage( imageUrl = item.avatar, @@ -450,7 +470,7 @@ fun ChatAiOtherItem(item: ChatItem) { max = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 250.dp else 150.dp) ) .clip(RoundedCornerShape(8.dp)) - .background(AppColors.background) + .background(AppColors.bubbleBackground) .padding( vertical = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 8.dp else 0.dp), horizontal = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 16.dp else 0.dp) @@ -463,7 +483,7 @@ fun ChatAiOtherItem(item: ChatItem) { text = item.message, style = TextStyle( color = AppColors.text, - fontSize = 16.sp, + fontSize = 14.sp, ), textAlign = TextAlign.Start ) @@ -510,6 +530,7 @@ fun ChatAiInput( onSendImage: (Uri?) -> Unit = {}, onSend: (String) -> Unit = {}, ) { + val context = LocalContext.current val navigationBarHeight = with(LocalDensity.current) { WindowInsets.navigationBars.getBottom(this).toDp() } @@ -525,9 +546,8 @@ fun ChatAiInput( ), label = "" ) - // 在 isKeyboardOpen 变化时立即更新 inputBarHeight 的动画目标值 LaunchedEffect(isKeyboardOpen) { - inputBarHeight // 触发 inputBarHeight 的重组 + inputBarHeight } val focusManager = LocalFocusManager.current val windowInsets = WindowInsets.ime @@ -549,71 +569,83 @@ fun ChatAiInput( onSendImage(uri) } } + Box( modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 16.dp, bottom = 12.dp),){ - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp) - .padding(bottom = inputBarHeight) - ) { - Box( + Row( modifier = Modifier - .weight(1f) - .clip(RoundedCornerShape(16.dp)) - .background(appColors.background) - .padding(horizontal = 16.dp), - contentAlignment = Alignment.CenterStart, + .fillMaxWidth() + .clip(RoundedCornerShape(20.dp)) + .background(appColors.decentBackground) + .padding(start = 16.dp, end = 8.dp, top = 2.dp, bottom = 2.dp), + verticalAlignment = Alignment.CenterVertically, ) { - BasicTextField( - value = text, - onValueChange = { - text = it - }, - textStyle = TextStyle( - color = appColors.text, - fontSize = 16.sp - ), - cursorBrush = SolidColor(appColors.text), + Box( modifier = Modifier - .fillMaxWidth() - .padding(vertical = 8.dp) - .onFocusChanged { focusState -> - isKeyboardOpen = focusState.isFocused - } - .pointerInput(Unit) { - awaitPointerEventScope { - keyboardController = softwareKeyboardController - awaitFirstDown().also { - keyboardController?.show() + .weight(1f) + ) { + BasicTextField( + value = text, + onValueChange = { + text = it + }, + textStyle = TextStyle( + color = appColors.text, + fontSize = 16.sp + ), + cursorBrush = SolidColor(appColors.text), + singleLine = true, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp) + .onFocusChanged { focusState -> + isKeyboardOpen = focusState.isFocused + } + .pointerInput(Unit) { + awaitPointerEventScope { + keyboardController = softwareKeyboardController + awaitFirstDown().also { + keyboardController?.show() + } } + }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions( + onDone = { + keyboardController?.hide() } - }, - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), - keyboardActions = KeyboardActions( - onDone = { - keyboardController?.hide() - } + ) ) - ) - } - Spacer(modifier = Modifier.width(8.dp)) - Crossfade( - targetState = text.isNotEmpty(), animationSpec = tween(500), - label = "" - ) { isNotEmpty -> - Icon( - painter = painterResource(id = R.drawable.rider_pro_video_share), - contentDescription = "Emoji", - modifier = Modifier - .size(32.dp) - .noRippleClickable { - if (text.isNotEmpty()) { - onSend(text) - text = "" - } - }, - tint = if (isNotEmpty) appColors.main else appColors.chatActionColor - ) + } + + Spacer(modifier = Modifier.width(8.dp)) + Crossfade( + targetState = text.isNotEmpty(), animationSpec = tween(500), + label = "" + ) { isNotEmpty -> + val alpha by animateFloatAsState( + targetValue = if (isNotEmpty) 1f else 0.5f, + animationSpec = tween(300) + ) + Image( + painter = painterResource(R.mipmap.rider_pro_im_send), + modifier = Modifier + .size(24.dp) + .alpha(alpha) + .noRippleClickable { + if (text.isNotEmpty()) { + if (NetworkUtils.isNetworkAvailable(context)) { + onSend(text) + text = "" + } else { + android.widget.Toast.makeText(context, "网络连接异常,请检查网络设置", android.widget.Toast.LENGTH_SHORT).show() + } + } + }, + contentDescription = null, + ) + } } } } diff --git a/app/src/main/java/com/aiosman/ravenow/ui/chat/ChatScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/chat/ChatScreen.kt index cf691b8..62b4485 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/chat/ChatScreen.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/chat/ChatScreen.kt @@ -83,6 +83,7 @@ import com.aiosman.ravenow.ui.composables.DropdownMenu import com.aiosman.ravenow.ui.composables.MenuItem import com.aiosman.ravenow.ui.composables.StatusBarSpacer import com.aiosman.ravenow.ui.modifiers.noRippleClickable +import com.aiosman.ravenow.utils.NetworkUtils import com.tencent.imsdk.v2.V2TIMMessage import kotlinx.coroutines.launch import java.util.UUID @@ -231,12 +232,16 @@ fun ChatScreen(userId: String) { ) { isMenuExpanded = false - viewModel.viewModelScope.launch { - if (viewModel.notificationStrategy == "mute") { - viewModel.updateNotificationStrategy("active") - } else { - viewModel.updateNotificationStrategy("mute") + if (NetworkUtils.isNetworkAvailable(context)) { + viewModel.viewModelScope.launch { + if (viewModel.notificationStrategy == "mute") { + viewModel.updateNotificationStrategy("active") + } else { + viewModel.updateNotificationStrategy("mute") + } } + } else { + android.widget.Toast.makeText(context, "网络连接异常,请检查网络设置", android.widget.Toast.LENGTH_SHORT).show() } } ), @@ -262,7 +267,11 @@ fun ChatScreen(userId: String) { ChatInput( onSendImage = { it?.let { - viewModel.sendImageMessage(it, context) + if (NetworkUtils.isNetworkAvailable(context)) { + viewModel.sendImageMessage(it, context) + } else { + android.widget.Toast.makeText(context, "网络连接异常,请检查网络设置", android.widget.Toast.LENGTH_SHORT).show() + } } }, ) { @@ -443,11 +452,11 @@ fun ChatOtherItem(item: ChatItem) { ) { Box( modifier = Modifier - .size(24.dp) - .clip(RoundedCornerShape(24.dp)) + .size(40.dp) + .clip(RoundedCornerShape(40.dp)) ) { CustomAsyncImage( - imageUrl = item.avatar.replace("storage/avatars/", "/avatar/"), + imageUrl = item.avatar, modifier = Modifier.fillMaxSize(), contentDescription = "avatar" ) @@ -460,12 +469,13 @@ fun ChatOtherItem(item: ChatItem) { min = 20.dp, max = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 250.dp else 150.dp) ) - .clip(RoundedCornerShape(20.dp)) - .background(AppColors.bubbleBackground) + .clip(RoundedCornerShape(8.dp)) + .background(AppColors.background) .padding( vertical = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 8.dp else 0.dp), horizontal = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 16.dp else 0.dp) ) + .padding(bottom = (if (item.messageType == V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) 3.dp else 0.dp)) ) { when (item.messageType) { V2TIMMessage.V2TIM_ELEM_TYPE_TEXT -> { @@ -473,7 +483,7 @@ fun ChatOtherItem(item: ChatItem) { text = item.message, style = TextStyle( color = AppColors.text, - fontSize = 14.sp, + fontSize = 16.sp, ), textAlign = TextAlign.Start ) @@ -489,7 +499,7 @@ fun ChatOtherItem(item: ChatItem) { else -> { Text( - text = "不支持的消息类型", + text = "Unsupported message type", style = TextStyle( color = AppColors.text, fontSize = 16.sp, @@ -498,6 +508,7 @@ fun ChatOtherItem(item: ChatItem) { } } } + } } @@ -519,6 +530,7 @@ fun ChatInput( onSendImage: (Uri?) -> Unit = {}, onSend: (String) -> Unit = {}, ) { + val context = LocalContext.current val navigationBarHeight = with(LocalDensity.current) { WindowInsets.navigationBars.getBottom(this).toDp() } @@ -614,14 +626,18 @@ fun ChatInput( modifier = Modifier .size(30.dp) .noRippleClickable { - imagePickUpLauncher.launch( - Intent.createChooser( - Intent(Intent.ACTION_GET_CONTENT).apply { - type = "image/*" - }, - "Select Image" + if (NetworkUtils.isNetworkAvailable(context)) { + imagePickUpLauncher.launch( + Intent.createChooser( + Intent(Intent.ACTION_GET_CONTENT).apply { + type = "image/*" + }, + "Select Image" + ) ) - ) + } else { + android.widget.Toast.makeText(context, "网络连接异常,请检查网络设置", android.widget.Toast.LENGTH_SHORT).show() + } }, ) @@ -634,19 +650,23 @@ fun ChatInput( targetValue = if (isNotEmpty) 1f else 0.5f, animationSpec = tween(300) ) - Image( - painter = painterResource(R.mipmap.rider_pro_im_send), - modifier = Modifier - .size(24.dp) - .alpha(alpha) - .noRippleClickable { - if (text.isNotEmpty()) { + Image( + painter = painterResource(R.mipmap.rider_pro_im_send), + modifier = Modifier + .size(24.dp) + .alpha(alpha) + .noRippleClickable { + if (text.isNotEmpty()) { + if (NetworkUtils.isNetworkAvailable(context)) { onSend(text) text = "" + } else { + android.widget.Toast.makeText(context, "网络连接异常,请检查网络设置", android.widget.Toast.LENGTH_SHORT).show() } - }, - contentDescription = null, - ) + } + }, + contentDescription = null, + ) } } } diff --git a/app/src/main/java/com/aiosman/ravenow/ui/chat/GroupChatScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/chat/GroupChatScreen.kt index 7fd6c89..24ed55f 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/chat/GroupChatScreen.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/chat/GroupChatScreen.kt @@ -85,6 +85,7 @@ import com.aiosman.ravenow.ui.composables.MenuItem import com.aiosman.ravenow.ui.composables.StatusBarSpacer import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.aiosman.ravenow.ui.navigateToGroupInfo +import com.aiosman.ravenow.utils.NetworkUtils import com.tencent.imsdk.v2.V2TIMMessage import kotlinx.coroutines.launch import java.util.UUID @@ -257,7 +258,11 @@ fun GroupChatScreen(groupId: String,name: String,avatar: String,) { GroupChatInput( onSendImage = { uri -> uri?.let { - viewModel.sendImageMessage(it, context) + if (NetworkUtils.isNetworkAvailable(context)) { + viewModel.sendImageMessage(it, context) + } else { + android.widget.Toast.makeText(context, "网络连接异常,请检查网络设置", android.widget.Toast.LENGTH_SHORT).show() + } } }, ) { message -> @@ -566,6 +571,7 @@ fun GroupChatInput( onSendImage: (Uri?) -> Unit = {}, onSend: (String) -> Unit = {}, ) { + val context = LocalContext.current val navigationBarHeight = with(LocalDensity.current) { WindowInsets.navigationBars.getBottom(this).toDp() } @@ -670,8 +676,12 @@ fun GroupChatInput( .alpha(alpha) .noRippleClickable { if (text.isNotEmpty()) { - onSend(text) - text = "" + if (NetworkUtils.isNetworkAvailable(context)) { + onSend(text) + text = "" + } else { + android.widget.Toast.makeText(context, "网络连接异常,请检查网络设置", android.widget.Toast.LENGTH_SHORT).show() + } } }, contentDescription = null, diff --git a/app/src/main/java/com/aiosman/ravenow/ui/composables/DropdownMenu.kt b/app/src/main/java/com/aiosman/ravenow/ui/composables/DropdownMenu.kt index 95105a3..ac04812 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/composables/DropdownMenu.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/composables/DropdownMenu.kt @@ -14,6 +14,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp @@ -24,7 +25,6 @@ data class MenuItem( val title: String, val icon: Int, val action: () -> Unit - ) @Composable @@ -36,50 +36,42 @@ fun DropdownMenu( ) { val AppColors = LocalAppTheme.current - MaterialTheme( - shapes = MaterialTheme.shapes.copy( - extraSmall = RoundedCornerShape( - 16.dp - ) - ) + androidx.compose.material3.DropdownMenu( + expanded = expanded, + onDismissRequest = onDismissRequest, + modifier = Modifier + .let { + if (width != null) it.width(width.dp) else it + } + .background(color = AppColors.background), ) { - androidx.compose.material3.DropdownMenu( - expanded = expanded, - onDismissRequest = onDismissRequest, - modifier = Modifier - .let { - if (width != null) it.width(width.dp) else it - } - .background(AppColors.background) - ) { - for (item in menuItems) { - Box( - modifier = Modifier - .padding(vertical = 14.dp, horizontal = 24.dp) - .noRippleClickable { - item.action() - }) { - Row( - verticalAlignment = Alignment.CenterVertically - ) { - Text( - item.title, - fontWeight = FontWeight.W500, - color = AppColors.text, - ) - if (width != null) { - Spacer(modifier = Modifier.weight(1f)) - - } else { - Spacer(modifier = Modifier.width(16.dp)) - } - Icon( - painter = painterResource(id = item.icon), - contentDescription = "", - modifier = Modifier.size(24.dp), - tint = AppColors.text - ) + for (item in menuItems) { + Box( + modifier = Modifier + .padding(vertical = 14.dp, horizontal = 24.dp) + .noRippleClickable { + item.action() } + ) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Text( + item.title, + fontWeight = FontWeight.W500, + color = AppColors.text, + ) + if (width != null) { + Spacer(modifier = Modifier.weight(1f)) + } else { + Spacer(modifier = Modifier.width(16.dp)) + } + Icon( + painter = painterResource(id = item.icon), + contentDescription = "", + modifier = Modifier.size(24.dp), + tint = AppColors.text + ) } } } diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt index 15df560..bcee8aa 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt @@ -363,7 +363,7 @@ fun IndexScreen() { @Composable fun Home() { val systemUiController = rememberSystemUiController() - LaunchedEffect(Unit) { + LaunchedEffect(AppState.darkMode) { systemUiController.setStatusBarColor(Color.Transparent, darkIcons = !AppState.darkMode) } Column( @@ -380,7 +380,7 @@ fun Home() { @Composable fun Street() { val systemUiController = rememberSystemUiController() - LaunchedEffect(Unit) { + LaunchedEffect(AppState.darkMode) { systemUiController.setStatusBarColor(Color.Transparent, darkIcons = !AppState.darkMode) } Column( @@ -396,7 +396,7 @@ fun Street() { @Composable fun Add() { val systemUiController = rememberSystemUiController() - LaunchedEffect(Unit) { + LaunchedEffect(AppState.darkMode) { systemUiController.setStatusBarColor(Color.Black, darkIcons = !AppState.darkMode) } Column( @@ -413,7 +413,7 @@ fun Add() { @Composable fun Video() { val systemUiController = rememberSystemUiController() - LaunchedEffect(Unit) { + LaunchedEffect(AppState.darkMode) { systemUiController.setStatusBarColor(Color.Black, darkIcons = false) } Column( @@ -430,7 +430,7 @@ fun Video() { @Composable fun Profile() { val systemUiController = rememberSystemUiController() - LaunchedEffect(Unit) { + LaunchedEffect(AppState.darkMode) { systemUiController.setStatusBarColor(Color.Transparent, !AppState.darkMode) } Column( @@ -446,7 +446,7 @@ fun Profile() { @Composable fun Notifications() { val systemUiController = rememberSystemUiController() - LaunchedEffect(Unit) { + LaunchedEffect(AppState.darkMode) { systemUiController.setStatusBarColor(Color.Transparent, !AppState.darkMode) } Column( diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AgentChatListScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AgentChatListScreen.kt index 741d5f5..b1e76d0 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AgentChatListScreen.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/AgentChatListScreen.kt @@ -44,6 +44,7 @@ import com.aiosman.ravenow.R import com.aiosman.ravenow.ui.NavigationRoute import com.aiosman.ravenow.ui.composables.CustomAsyncImage import com.aiosman.ravenow.ui.modifiers.noRippleClickable +import com.aiosman.ravenow.utils.NetworkUtils /** * 智能体聊天列表页面 @@ -123,8 +124,12 @@ fun AgentChatListScreen() { AgentChatListViewModel.goToUserDetail(conv, navController) }, onChatClick = { conv -> - model.createSingleChat(conv.trtcUserId) - model.goToChatAi(conv.trtcUserId,navController) + if (NetworkUtils.isNetworkAvailable(context)) { + model.createSingleChat(conv.trtcUserId) + model.goToChatAi(conv.trtcUserId,navController) + } else { + android.widget.Toast.makeText(context, "网络连接异常,请检查网络设置", android.widget.Toast.LENGTH_SHORT).show() + } } ) diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/FriendChatListScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/FriendChatListScreen.kt index 6c19e92..1c084ad 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/FriendChatListScreen.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/FriendChatListScreen.kt @@ -32,6 +32,7 @@ import com.aiosman.ravenow.LocalNavController import com.aiosman.ravenow.R import com.aiosman.ravenow.ui.composables.CustomAsyncImage import com.aiosman.ravenow.ui.modifiers.noRippleClickable +import com.aiosman.ravenow.utils.NetworkUtils @OptIn(ExperimentalMaterialApi::class) @Composable @@ -106,7 +107,11 @@ fun FriendChatListScreen() { FriendChatListViewModel.goToUserDetail(conv, navController) }, onChatClick = { conv -> - FriendChatListViewModel.goToChat(conv, navController) + if (NetworkUtils.isNetworkAvailable(context)) { + FriendChatListViewModel.goToChat(conv, navController) + } else { + android.widget.Toast.makeText(context, "网络连接异常,请检查网络设置", android.widget.Toast.LENGTH_SHORT).show() + } } ) diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/GroupChatListScreen.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/GroupChatListScreen.kt index 3c58c4c..5aab6b0 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/GroupChatListScreen.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/message/tab/GroupChatListScreen.kt @@ -1,8 +1,6 @@ package com.aiosman.ravenow.ui.index.tabs.message.tab -import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed @@ -16,14 +14,11 @@ 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.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow @@ -34,6 +29,7 @@ import com.aiosman.ravenow.LocalNavController import com.aiosman.ravenow.R import com.aiosman.ravenow.ui.composables.CustomAsyncImage import com.aiosman.ravenow.ui.modifiers.noRippleClickable +import com.aiosman.ravenow.utils.NetworkUtils @OptIn(ExperimentalMaterialApi::class) @Composable @@ -51,15 +47,6 @@ fun GroupChatListScreen() { LaunchedEffect(Unit) { GroupChatListViewModel.refreshPager(context = context) - // 初始化消息监听器 - GroupChatListViewModel.initMessageListener(context) - } - - // 在组件销毁时清理监听器 - DisposableEffect(Unit) { - onDispose { - GroupChatListViewModel.removeMessageListener() - } } @@ -79,31 +66,18 @@ fun GroupChatListScreen() { .fillMaxSize() .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally, - + verticalArrangement = Arrangement.Center ) { - - Spacer(modifier = Modifier.height(80.dp)) - Image( - painter = painterResource(id = if (isSystemInDarkTheme()) { - R.mipmap.icon_group_chat_empty // 深色模式图片 - } else { - R.mipmap.icon_group_chat_empty // 浅色模式图片 - }), - contentDescription = "null data", - modifier = Modifier - .size(140.dp) - ) - Spacer(modifier = Modifier.height(24.dp)) Text( - text = stringResource(R.string.group_chat_empty_title), + text = "暂无群聊", color = AppColors.text, fontSize = 16.sp, fontWeight = FontWeight.W600 ) Spacer(modifier = Modifier.height(8.dp)) Text( - text = stringResource(R.string.group_chat_empty_subtitle), - color = AppColors.text, + text = "您还没有加入任何群聊", + color = AppColors.secondaryText, fontSize = 14.sp ) } @@ -121,10 +95,20 @@ fun GroupChatListScreen() { GroupChatListViewModel.goToGroupDetail(conv, navController) }, onChatClick = { conv -> - GroupChatListViewModel.goToChat(conv, navController) + if (NetworkUtils.isNetworkAvailable(context)) { + GroupChatListViewModel.goToChat(conv, navController) + } else { + android.widget.Toast.makeText(context, "网络连接异常,请检查网络设置", android.widget.Toast.LENGTH_SHORT).show() + } } ) + if (index < GroupChatListViewModel.groupChatList.size - 1) { + HorizontalDivider( + modifier = Modifier.padding(horizontal = 24.dp), + color = AppColors.divider + ) + } } if (GroupChatListViewModel.isLoading && GroupChatListViewModel.groupChatList.isNotEmpty()) { @@ -180,11 +164,10 @@ fun GroupChatItem( Row( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 12.dp) + .padding(horizontal = 24.dp, vertical = 12.dp) .noRippleClickable { onChatClick(conversation) - }, - verticalAlignment = Alignment.CenterVertically + } ) { Box { CustomAsyncImage( @@ -202,9 +185,9 @@ fun GroupChatItem( Column( modifier = Modifier + .weight(1f) - .padding(start = 12.dp, top = 2.dp), - verticalArrangement = Arrangement.Center + .padding(start = 12.dp) ) { Row( modifier = Modifier.fillMaxWidth(), @@ -212,22 +195,22 @@ fun GroupChatItem( ) { Text( text = conversation.groupName, - fontSize = 14.sp, + fontSize = 16.sp, fontWeight = FontWeight.Bold, color = AppColors.text, modifier = Modifier.weight(1f) ) - Spacer(modifier = Modifier.width(6.dp)) + Spacer(modifier = Modifier.width(8.dp)) Text( text = conversation.lastMessageTime, - fontSize = 11.sp, + fontSize = 12.sp, color = AppColors.secondaryText ) } - Spacer(modifier = Modifier.height(6.dp)) + Spacer(modifier = Modifier.height(4.dp)) Row( modifier = Modifier.fillMaxWidth(), @@ -235,29 +218,30 @@ fun GroupChatItem( ) { Text( text = "${if (conversation.isSelf) stringResource(R.string.friend_chat_me_prefix) else ""}${conversation.displayText}", - fontSize = 12.sp, + fontSize = 14.sp, color = AppColors.secondaryText, maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = Modifier.weight(1f) ) - Spacer(modifier = Modifier.width(10.dp)) + Spacer(modifier = Modifier.width(8.dp)) if (conversation.unreadCount > 0) { Box( modifier = Modifier .size(if (conversation.unreadCount > 99) 24.dp else 20.dp) .background( - color = Color(0xFFFF3B30), + color = AppColors.main, shape = CircleShape ), contentAlignment = Alignment.Center ) { Text( text = if (conversation.unreadCount > 99) "99+" else conversation.unreadCount.toString(), - color = Color.White, - fontSize = if (conversation.unreadCount > 99) 11.sp else 12.sp, + color = AppColors.mainText, + fontSize = if (conversation.unreadCount > 99) 9.sp else 10.sp, + fontWeight = FontWeight.Bold ) } } diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/profile/ProfileV3.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/profile/ProfileV3.kt index d38c960..015911b 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/profile/ProfileV3.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/profile/ProfileV3.kt @@ -26,6 +26,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.CircleShape @@ -140,6 +141,7 @@ fun ProfileV3( val AppColors = appTheme val systemUiController = rememberSystemUiController() val listState = rememberLazyListState() + val gridState = rememberLazyGridState() // observe list scrolling val reachedListBottom by remember { @@ -149,13 +151,28 @@ fun ProfileV3( } } - // load more if scrolled to bottom + // observe grid scrolling for load more + val reachedGridBottom by remember { + derivedStateOf { + val lastVisibleItem = gridState.layoutInfo.visibleItemsInfo.lastOrNull() + lastVisibleItem?.index != 0 && lastVisibleItem?.index == gridState.layoutInfo.totalItemsCount - 2 + } + } + + // load more if scrolled to bottom of list LaunchedEffect(reachedListBottom) { if (reachedListBottom) { onLoadMore() } } + // load more if scrolled to bottom of grid + LaunchedEffect(reachedGridBottom) { + if (reachedGridBottom) { + onLoadMore() + } + } + fun switchTheme() { @@ -451,15 +468,16 @@ fun ProfileV3( pagerState = pagerState, ) Spacer(modifier = Modifier.height(8.dp)) - HorizontalPager( - state = pagerState, - ) { idx -> - when (idx) { - 0 -> - LazyVerticalGrid( - columns = GridCells.Fixed(3), - modifier = Modifier.fillMaxSize().padding(bottom = 8.dp), - ) { + HorizontalPager( + state = pagerState, + ) { idx -> + when (idx) { + 0 -> + LazyVerticalGrid( + columns = GridCells.Fixed(3), + state = gridState, + modifier = Modifier.fillMaxSize().padding(bottom = 8.dp), + ) { items(moments.size) { idx -> val moment = moments[idx] ?: return@items diff --git a/app/src/main/java/com/aiosman/ravenow/utils/NetworkUtils.kt b/app/src/main/java/com/aiosman/ravenow/utils/NetworkUtils.kt new file mode 100644 index 0000000..fb21f1f --- /dev/null +++ b/app/src/main/java/com/aiosman/ravenow/utils/NetworkUtils.kt @@ -0,0 +1,71 @@ +package com.aiosman.ravenow.utils + +import android.content.Context +import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkCapabilities +import android.net.NetworkRequest +import android.os.Build +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.distinctUntilChanged + +object NetworkUtils { + + /** + * 检查当前网络是否可用 + */ + fun isNetworkAvailable(context: Context): Boolean { + val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + val network = connectivityManager.activeNetwork ?: return false + val activeNetwork = connectivityManager.getNetworkCapabilities(network) ?: return false + + activeNetwork.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && + activeNetwork.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + } else { + @Suppress("DEPRECATION") + val networkInfo = connectivityManager.activeNetworkInfo + @Suppress("DEPRECATION") + networkInfo?.isConnected == true + } + } + + /** + * 获取网络状态变化的Flow + */ + fun getNetworkStateFlow(context: Context): Flow = callbackFlow { + val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + + val networkCallback = object : ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + trySend(true) + } + + override fun onLost(network: Network) { + trySend(false) + } + + override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { + val hasInternet = networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && + networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + trySend(hasInternet) + } + } + + val networkRequest = NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .build() + + connectivityManager.registerNetworkCallback(networkRequest, networkCallback) + + // 发送初始状态 + trySend(isNetworkAvailable(context)) + + awaitClose { + connectivityManager.unregisterNetworkCallback(networkCallback) + } + }.distinctUntilChanged() +}