更新聊天的滚动逻辑
This commit is contained in:
@@ -48,8 +48,10 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.runtime.rememberUpdatedState
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.draw.shadow
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
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.composables.StatusBarSpacer
|
||||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||||
import com.tencent.imsdk.v2.V2TIMMessage
|
import com.tencent.imsdk.v2.V2TIMMessage
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
@@ -86,6 +89,7 @@ fun ChatScreen(userId: String) {
|
|||||||
var isMenuExpanded by remember { mutableStateOf(false) }
|
var isMenuExpanded by remember { mutableStateOf(false) }
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
val context = LocalNavController.current.context
|
val context = LocalNavController.current.context
|
||||||
|
var goToNewCount by remember { mutableStateOf(0) }
|
||||||
val viewModel = viewModel<ChatViewModel>(
|
val viewModel = viewModel<ChatViewModel>(
|
||||||
key = "ChatViewModel_$userId",
|
key = "ChatViewModel_$userId",
|
||||||
factory = object : ViewModelProvider.Factory {
|
factory = object : ViewModelProvider.Factory {
|
||||||
@@ -109,17 +113,44 @@ fun ChatScreen(userId: String) {
|
|||||||
val navigationBarHeight = with(LocalDensity.current) {
|
val navigationBarHeight = with(LocalDensity.current) {
|
||||||
WindowInsets.navigationBars.getBottom(this).toDp()
|
WindowInsets.navigationBars.getBottom(this).toDp()
|
||||||
}
|
}
|
||||||
|
var inBottom by remember { mutableStateOf(true) }
|
||||||
// 监听滚动状态,触发加载更多
|
// 监听滚动状态,触发加载更多
|
||||||
LaunchedEffect(listState) {
|
LaunchedEffect(listState) {
|
||||||
snapshotFlow { listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index }
|
snapshotFlow { listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index }
|
||||||
.collect { index ->
|
.collect { index ->
|
||||||
|
Log.d("ChatScreen", "lastVisibleItemIndex: ${index}")
|
||||||
if (index == listState.layoutInfo.totalItemsCount - 1) {
|
if (index == listState.layoutInfo.totalItemsCount - 1) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
viewModel.onLoadMore(context)
|
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(
|
Scaffold(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -128,7 +159,6 @@ fun ChatScreen(userId: String) {
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.border(1.dp, Color(0xffe5e5e5))
|
|
||||||
.background(Color.White)
|
.background(Color.White)
|
||||||
) {
|
) {
|
||||||
StatusBarSpacer()
|
StatusBarSpacer()
|
||||||
@@ -137,7 +167,7 @@ fun ChatScreen(userId: String) {
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(vertical = 16.dp, horizontal = 16.dp),
|
.padding(vertical = 16.dp, horizontal = 16.dp),
|
||||||
horizontalArrangement = Arrangement.Start,
|
horizontalArrangement = Arrangement.Start,
|
||||||
verticalAlignment = androidx.compose.ui.Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Image(
|
Image(
|
||||||
painter = painterResource(R.drawable.rider_pro_nav_back),
|
painter = painterResource(R.drawable.rider_pro_nav_back),
|
||||||
@@ -221,11 +251,15 @@ fun ChatScreen(userId: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color(0xfff7f7f7))
|
||||||
|
.padding(paddingValues)
|
||||||
|
) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
state = listState,
|
state = listState,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(paddingValues)
|
|
||||||
.background(Color(0xfff7f7f7))
|
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
reverseLayout = true,
|
reverseLayout = true,
|
||||||
verticalArrangement = Arrangement.Top
|
verticalArrangement = Arrangement.Top
|
||||||
@@ -248,10 +282,44 @@ fun ChatScreen(userId: String) {
|
|||||||
.padding(vertical = 8.dp)
|
.padding(vertical = 8.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatItem(item = item, viewModel.myProfile?.trtcUserId!!)
|
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 = "${goToNewCount} New Message",
|
||||||
|
style = TextStyle(
|
||||||
|
color = Color.Black,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class ChatViewModel(
|
|||||||
var lastMessage: V2TIMMessage? = null
|
var lastMessage: V2TIMMessage? = null
|
||||||
val showTimestampMap = mutableMapOf<String, Boolean>() // Add this map
|
val showTimestampMap = mutableMapOf<String, Boolean>() // Add this map
|
||||||
var chatNotification by mutableStateOf<ChatNotification?>(null)
|
var chatNotification by mutableStateOf<ChatNotification?>(null)
|
||||||
|
var goToNew by mutableStateOf(false)
|
||||||
fun init(context: Context) {
|
fun init(context: Context) {
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@@ -68,6 +69,7 @@ class ChatViewModel(
|
|||||||
val chatItem = ChatItem.convertToChatItem(msg, context)
|
val chatItem = ChatItem.convertToChatItem(msg, context)
|
||||||
chatItem?.let {
|
chatItem?.let {
|
||||||
chatData = listOf(it) + chatData
|
chatData = listOf(it) + chatData
|
||||||
|
goToNew = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,7 +96,6 @@ class ChatViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun onLoadMore(context: Context) {
|
fun onLoadMore(context: Context) {
|
||||||
if (!hasMore || isLoading) {
|
if (!hasMore || isLoading) {
|
||||||
return
|
return
|
||||||
@@ -145,6 +146,7 @@ class ChatViewModel(
|
|||||||
val chatItem = ChatItem.convertToChatItem(p0!!, context)
|
val chatItem = ChatItem.convertToChatItem(p0!!, context)
|
||||||
chatItem?.let {
|
chatItem?.let {
|
||||||
chatData = listOf(it) + chatData
|
chatData = listOf(it) + chatData
|
||||||
|
goToNew = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,6 +179,7 @@ class ChatViewModel(
|
|||||||
val chatItem = ChatItem.convertToChatItem(p0!!, context)
|
val chatItem = ChatItem.convertToChatItem(p0!!, context)
|
||||||
chatItem?.let {
|
chatItem?.let {
|
||||||
chatData = listOf(it) + chatData
|
chatData = listOf(it) + chatData
|
||||||
|
goToNew = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,6 +187,7 @@ class ChatViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createTempFile(context: Context, uri: Uri): File? {
|
fun createTempFile(context: Context, uri: Uri): File? {
|
||||||
return try {
|
return try {
|
||||||
val projection = arrayOf(MediaStore.Images.Media.DATA)
|
val projection = arrayOf(MediaStore.Images.Media.DATA)
|
||||||
@@ -195,7 +199,8 @@ class ChatViewModel(
|
|||||||
val inputStream: InputStream? = context.contentResolver.openInputStream(uri)
|
val inputStream: InputStream? = context.contentResolver.openInputStream(uri)
|
||||||
val mimeType = context.contentResolver.getType(uri)
|
val mimeType = context.contentResolver.getType(uri)
|
||||||
val extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType)
|
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)
|
val outputStream = FileOutputStream(tempFile)
|
||||||
|
|
||||||
inputStream?.use { input ->
|
inputStream?.use { input ->
|
||||||
|
|||||||
Reference in New Issue
Block a user