更新聊天的滚动逻辑
This commit is contained in:
@@ -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<ChatViewModel>(
|
||||
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!!)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ class ChatViewModel(
|
||||
var lastMessage: V2TIMMessage? = null
|
||||
val showTimestampMap = mutableMapOf<String, Boolean>() // Add this map
|
||||
var chatNotification by mutableStateOf<ChatNotification?>(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
|
||||
|
||||
Reference in New Issue
Block a user