From dc363122c97674af3face16527529f2f0d0a7244 Mon Sep 17 00:00:00 2001 From: AllenTom Date: Sat, 12 Oct 2024 16:29:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=81=8A=E5=A4=A9=E7=9A=84?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aiosman/riderpro/ui/chat/ChatScreen.kt | 116 ++++++++++++++---- .../aiosman/riderpro/ui/chat/ChatViewModel.kt | 15 ++- 2 files changed, 102 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/com/aiosman/riderpro/ui/chat/ChatScreen.kt b/app/src/main/java/com/aiosman/riderpro/ui/chat/ChatScreen.kt index e01a56d..2985c76 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/chat/ChatScreen.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/chat/ChatScreen.kt @@ -48,8 +48,10 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberUpdatedState 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.clip +import androidx.compose.ui.draw.shadow import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput @@ -78,6 +80,7 @@ import com.aiosman.riderpro.ui.composables.MenuItem import com.aiosman.riderpro.ui.composables.StatusBarSpacer import com.aiosman.riderpro.ui.modifiers.noRippleClickable import com.tencent.imsdk.v2.V2TIMMessage +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch @@ -86,6 +89,7 @@ fun ChatScreen(userId: String) { var isMenuExpanded by remember { mutableStateOf(false) } val navController = LocalNavController.current val context = LocalNavController.current.context + var goToNewCount by remember { mutableStateOf(0) } val viewModel = viewModel( key = "ChatViewModel_$userId", factory = object : ViewModelProvider.Factory { @@ -109,17 +113,44 @@ fun ChatScreen(userId: String) { val navigationBarHeight = with(LocalDensity.current) { WindowInsets.navigationBars.getBottom(this).toDp() } + var inBottom by remember { mutableStateOf(true) } // 监听滚动状态,触发加载更多 LaunchedEffect(listState) { snapshotFlow { listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index } .collect { index -> + Log.d("ChatScreen", "lastVisibleItemIndex: ${index}") if (index == listState.layoutInfo.totalItemsCount - 1) { coroutineScope.launch { viewModel.onLoadMore(context) } } + } } + // 监听滚动状态,触发滚动到底部 + LaunchedEffect(listState) { + snapshotFlow { listState.layoutInfo.visibleItemsInfo.firstOrNull()?.index } + .collect { index -> + inBottom = index == 0 + if (index == 0) { + goToNewCount = 0 + } + } + } + + + // 监听是否需要滚动到最新消息 + LaunchedEffect(viewModel.goToNew) { + if (viewModel.goToNew) { + if (inBottom) { + listState.scrollToItem(0) + } else { + goToNewCount++ + } + viewModel.goToNew = false + } + } + Scaffold( modifier = Modifier @@ -128,7 +159,6 @@ fun ChatScreen(userId: String) { Column( modifier = Modifier .fillMaxWidth() - .border(1.dp, Color(0xffe5e5e5)) .background(Color.White) ) { StatusBarSpacer() @@ -137,7 +167,7 @@ fun ChatScreen(userId: String) { .fillMaxWidth() .padding(vertical = 16.dp, horizontal = 16.dp), horizontalArrangement = Arrangement.Start, - verticalAlignment = androidx.compose.ui.Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically ) { Image( painter = painterResource(R.drawable.rider_pro_nav_back), @@ -178,7 +208,7 @@ fun ChatScreen(userId: String) { MenuItem( title = if (viewModel.notificationStrategy == "mute") "Unmute" else "Mute", icon = if (viewModel.notificationStrategy == "mute") R.drawable.rider_pro_notice_mute else R.drawable.rider_pro_notice_active, - ){ + ) { isMenuExpanded = false viewModel.viewModelScope.launch { @@ -191,7 +221,7 @@ fun ChatScreen(userId: String) { } ), - ) + ) } } } @@ -221,36 +251,74 @@ fun ChatScreen(userId: String) { } } ) { paddingValues -> - LazyColumn( - state = listState, + Box( modifier = Modifier - .padding(paddingValues) + .fillMaxSize() .background(Color(0xfff7f7f7)) - .fillMaxSize(), - reverseLayout = true, - verticalArrangement = Arrangement.Top + .padding(paddingValues) ) { - val chatList = groupMessagesByTime(viewModel.getDisplayChatList(), viewModel) - items(chatList.size, key = { index -> chatList[index].msgId }) { index -> - val item = chatList[index] - if (item.showTimeDivider) { - val calendar = java.util.Calendar.getInstance() - calendar.timeInMillis = item.timestamp + LazyColumn( + state = listState, + modifier = Modifier + .fillMaxSize(), + reverseLayout = true, + verticalArrangement = Arrangement.Top + ) { + val chatList = groupMessagesByTime(viewModel.getDisplayChatList(), viewModel) + items(chatList.size, key = { index -> chatList[index].msgId }) { index -> + val item = chatList[index] + if (item.showTimeDivider) { + val calendar = java.util.Calendar.getInstance() + calendar.timeInMillis = item.timestamp + Text( + text = calendar.time.formatChatTime(context), // Format the timestamp + style = TextStyle( + color = Color.Gray, + fontSize = 14.sp, + textAlign = TextAlign.Center + ), + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp) + ) + } + + ChatItem(item = item, viewModel.myProfile?.trtcUserId!!) + + + } +// item { +// Spacer(modifier = Modifier.height(72.dp)) +// } + } + if (goToNewCount > 0) { + Box( + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(bottom = 16.dp, end = 16.dp) + .shadow(4.dp, shape = RoundedCornerShape(16.dp)) + .clip(RoundedCornerShape(16.dp)) + .background(Color.White) + .padding(8.dp) + .noRippleClickable { + coroutineScope.launch { + listState.scrollToItem(0) + } + }, + + ) { Text( - text = calendar.time.formatChatTime(context), // Format the timestamp + text = "${goToNewCount} New Message", style = TextStyle( - color = Color.Gray, - fontSize = 14.sp, - textAlign = TextAlign.Center + color = Color.Black, + fontSize = 16.sp, ), - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 8.dp) ) } - ChatItem(item = item, viewModel.myProfile?.trtcUserId!!) } } + + } } diff --git a/app/src/main/java/com/aiosman/riderpro/ui/chat/ChatViewModel.kt b/app/src/main/java/com/aiosman/riderpro/ui/chat/ChatViewModel.kt index 66f0fec..1082ad7 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/chat/ChatViewModel.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/chat/ChatViewModel.kt @@ -44,6 +44,7 @@ class ChatViewModel( var lastMessage: V2TIMMessage? = null val showTimestampMap = mutableMapOf() // Add this map var chatNotification by mutableStateOf(null) + var goToNew by mutableStateOf(false) fun init(context: Context) { // 获取用户信息 viewModelScope.launch { @@ -68,6 +69,7 @@ class ChatViewModel( val chatItem = ChatItem.convertToChatItem(msg, context) chatItem?.let { chatData = listOf(it) + chatData + goToNew = true } } } @@ -94,7 +96,6 @@ class ChatViewModel( } - fun onLoadMore(context: Context) { if (!hasMore || isLoading) { return @@ -145,13 +146,14 @@ class ChatViewModel( val chatItem = ChatItem.convertToChatItem(p0!!, context) chatItem?.let { chatData = listOf(it) + chatData + goToNew = true } } } ) } - fun sendImageMessage(imageUri:Uri, context: Context) { + fun sendImageMessage(imageUri: Uri, context: Context) { val tempFile = createTempFile(context, imageUri) val imagePath = tempFile?.path if (imagePath != null) { @@ -177,13 +179,15 @@ class ChatViewModel( val chatItem = ChatItem.convertToChatItem(p0!!, context) chatItem?.let { chatData = listOf(it) + chatData - } + goToNew = true + } } } ) } } + fun createTempFile(context: Context, uri: Uri): File? { return try { val projection = arrayOf(MediaStore.Images.Media.DATA) @@ -195,7 +199,8 @@ class ChatViewModel( val inputStream: InputStream? = context.contentResolver.openInputStream(uri) val mimeType = context.contentResolver.getType(uri) val extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType) - val tempFile = File.createTempFile("temp_image", ".$extension", context.cacheDir) + val tempFile = + File.createTempFile("temp_image", ".$extension", context.cacheDir) val outputStream = FileOutputStream(tempFile) inputStream?.use { input -> @@ -247,7 +252,7 @@ class ChatViewModel( return list } - suspend fun updateNotificationStrategy(strategy: String) { + suspend fun updateNotificationStrategy(strategy: String) { userProfile?.let { val result = ChatState.updateChatNotification(it.id, strategy) chatNotification = result