diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt index 64872cd70..b9089ea7d 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt @@ -43,6 +43,7 @@ import com.google.accompanist.insets.navigationBarsWithImePadding import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import kotlinx.datetime.Clock +import kotlin.math.roundToInt @Composable fun ChatView(chatModel: ChatModel) { @@ -470,6 +471,13 @@ fun BoxWithConstraintsScope.ChatItemsList( Spacer(Modifier.size(8.dp)) val reversedChatItems by remember { derivedStateOf { chatItems.reversed() } } + val maxHeightRounded = with(LocalDensity.current) { maxHeight.roundToPx() } + val scrollToItem: (Long) -> Unit = { itemId: Long -> + val index = reversedChatItems.indexOfFirst { it.id == itemId } + if (index != -1) { + scope.launch { listState.animateScrollToItem(kotlin.math.min(reversedChatItems.lastIndex, index + 1), -maxHeightRounded / 2) } + } + } LazyColumn(Modifier.align(Alignment.BottomCenter), state = listState, reverseLayout = true) { itemsIndexed(reversedChatItems) { i, cItem -> CompositionLocalProvider( @@ -523,11 +531,11 @@ fun BoxWithConstraintsScope.ChatItemsList( } else { Spacer(Modifier.size(42.dp)) } - ChatItemView(user, chat.chatInfo, cItem, composeState, cxt, uriHandler, showMember = showMember, chatModelIncognito = chatModelIncognito, useLinkPreviews = useLinkPreviews, deleteMessage = deleteMessage, receiveFile = receiveFile, joinGroup = {}, acceptCall = acceptCall) + ChatItemView(user, chat.chatInfo, cItem, composeState, cxt, uriHandler, showMember = showMember, chatModelIncognito = chatModelIncognito, useLinkPreviews = useLinkPreviews, deleteMessage = deleteMessage, receiveFile = receiveFile, joinGroup = {}, acceptCall = acceptCall, scrollToItem = scrollToItem) } } else { Box(Modifier.padding(start = 86.dp, end = 12.dp).then(swipeableModifier)) { - ChatItemView(user, chat.chatInfo, cItem, composeState, cxt, uriHandler, chatModelIncognito = chatModelIncognito, useLinkPreviews = useLinkPreviews, deleteMessage = deleteMessage, receiveFile = receiveFile, joinGroup = {}, acceptCall = acceptCall) + ChatItemView(user, chat.chatInfo, cItem, composeState, cxt, uriHandler, chatModelIncognito = chatModelIncognito, useLinkPreviews = useLinkPreviews, deleteMessage = deleteMessage, receiveFile = receiveFile, joinGroup = {}, acceptCall = acceptCall, scrollToItem = scrollToItem) } } } else { // direct message @@ -538,7 +546,7 @@ fun BoxWithConstraintsScope.ChatItemsList( end = if (sent) 12.dp else 76.dp, ).then(swipeableModifier) ) { - ChatItemView(user, chat.chatInfo, cItem, composeState, cxt, uriHandler, chatModelIncognito = chatModelIncognito, useLinkPreviews = useLinkPreviews, deleteMessage = deleteMessage, receiveFile = receiveFile, joinGroup = joinGroup, acceptCall = acceptCall) + ChatItemView(user, chat.chatInfo, cItem, composeState, cxt, uriHandler, chatModelIncognito = chatModelIncognito, useLinkPreviews = useLinkPreviews, deleteMessage = deleteMessage, receiveFile = receiveFile, joinGroup = joinGroup, acceptCall = acceptCall, scrollToItem = scrollToItem) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt index 7e21417cd..86518473e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt @@ -40,7 +40,8 @@ fun ChatItemView( deleteMessage: (Long, CIDeleteMode) -> Unit, receiveFile: (Long) -> Unit, joinGroup: (Long) -> Unit, - acceptCall: (Contact) -> Unit + acceptCall: (Contact) -> Unit, + scrollToItem: (Long) -> Unit, ) { val context = LocalContext.current val sent = cItem.chatDir.sent @@ -63,7 +64,7 @@ fun ChatItemView( EmojiItemView(cItem) } else { val onLinkLongClick = { _: String -> showMenu.value = true } - FramedItemView(cInfo, cItem, uriHandler, showMember = showMember, showMenu, receiveFile, onLinkLongClick) + FramedItemView(cInfo, cItem, uriHandler, showMember = showMember, showMenu, receiveFile, onLinkLongClick, scrollToItem) } DropdownMenu( expanded = showMenu.value, @@ -218,7 +219,8 @@ fun PreviewChatItemView() { deleteMessage = { _, _ -> }, receiveFile = {}, joinGroup = {}, - acceptCall = { _ -> } + acceptCall = { _ -> }, + scrollToItem = {}, ) } } @@ -238,7 +240,8 @@ fun PreviewChatItemViewDeletedContent() { deleteMessage = { _, _ -> }, receiveFile = {}, joinGroup = {}, - acceptCall = { _ -> } + acceptCall = { _ -> }, + scrollToItem = {}, ) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/FramedItemView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/FramedItemView.kt index 1b6ddba94..737cf4550 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/FramedItemView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/FramedItemView.kt @@ -40,7 +40,8 @@ fun FramedItemView( showMember: Boolean = false, showMenu: MutableState, receiveFile: (Long) -> Unit, - onLinkLongClick: (link: String) -> Unit = {} + onLinkLongClick: (link: String) -> Unit = {}, + scrollToItem: (Long) -> Unit = {}, ) { val sent = ci.chatDir.sent @@ -67,6 +68,7 @@ fun FramedItemView( Modifier .background(if (sent) SentQuoteColorLight else ReceivedQuoteColorLight) .fillMaxWidth() + .clickable { scrollToItem(qi.itemId?: return@clickable) } ) { when (qi.content) { is MsgContent.MCImage -> {