mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-06-29 04:39:53 +00:00
android, desktop: highlight quoted messaged on click to scroll to it (#5229)
This commit is contained in:
parent
ea9ee987cf
commit
bff2d7d3b6
2 changed files with 36 additions and 5 deletions
|
@ -980,8 +980,9 @@ fun BoxScope.ChatItemsList(
|
||||||
}
|
}
|
||||||
|
|
||||||
val chatInfoUpdated = rememberUpdatedState(chatInfo)
|
val chatInfoUpdated = rememberUpdatedState(chatInfo)
|
||||||
|
val highlightedItems = remember { mutableStateOf(setOf<Long>()) }
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val scrollToItem: (Long) -> Unit = remember { scrollToItem(loadingMoreItems, chatInfoUpdated, maxHeight, scope, reversedChatItems, mergedItems, listState, loadMessages) }
|
val scrollToItem: (Long) -> Unit = remember { scrollToItem(loadingMoreItems, highlightedItems, chatInfoUpdated, maxHeight, scope, reversedChatItems, mergedItems, listState, loadMessages) }
|
||||||
|
|
||||||
LoadLastItems(loadingMoreItems, remoteHostId, chatInfo)
|
LoadLastItems(loadingMoreItems, remoteHostId, chatInfo)
|
||||||
SmallScrollOnNewMessage(listState, chatModel.chatItems)
|
SmallScrollOnNewMessage(listState, chatModel.chatItems)
|
||||||
|
@ -1031,7 +1032,17 @@ fun BoxScope.ChatItemsList(
|
||||||
tryOrShowError("${cItem.id}ChatItem", error = {
|
tryOrShowError("${cItem.id}ChatItem", error = {
|
||||||
CIBrokenComposableView(if (cItem.chatDir.sent) Alignment.CenterEnd else Alignment.CenterStart)
|
CIBrokenComposableView(if (cItem.chatDir.sent) Alignment.CenterEnd else Alignment.CenterStart)
|
||||||
}) {
|
}) {
|
||||||
ChatItemView(remoteHostId, chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, range = range, fillMaxWidth = fillMaxWidth, selectedChatItems = selectedChatItems, selectChatItem = { selectUnselectChatItem(true, cItem, revealed, selectedChatItems) }, deleteMessage = deleteMessage, deleteMessages = deleteMessages, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, forwardItem = forwardItem, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails, reveal = reveal, developerTools = developerTools, showViaProxy = showViaProxy, itemSeparation = itemSeparation, showTimestamp = itemSeparation.timestamp)
|
val highlighted = remember { derivedStateOf { highlightedItems.value.contains(cItem.id) } }
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
snapshotFlow { highlighted.value }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.filter { it }
|
||||||
|
.collect {
|
||||||
|
delay(500)
|
||||||
|
highlightedItems.value = setOf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ChatItemView(remoteHostId, chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, highlighted = highlighted, range = range, fillMaxWidth = fillMaxWidth, selectedChatItems = selectedChatItems, selectChatItem = { selectUnselectChatItem(true, cItem, revealed, selectedChatItems) }, deleteMessage = deleteMessage, deleteMessages = deleteMessages, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, forwardItem = forwardItem, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails, reveal = reveal, developerTools = developerTools, showViaProxy = showViaProxy, itemSeparation = itemSeparation, showTimestamp = itemSeparation.timestamp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1810,6 +1821,7 @@ private fun lastFullyVisibleIemInListState(topPaddingToContentPx: State<Int>, de
|
||||||
|
|
||||||
private fun scrollToItem(
|
private fun scrollToItem(
|
||||||
loadingMoreItems: MutableState<Boolean>,
|
loadingMoreItems: MutableState<Boolean>,
|
||||||
|
highlightedItems: MutableState<Set<Long>>,
|
||||||
chatInfo: State<ChatInfo>,
|
chatInfo: State<ChatInfo>,
|
||||||
maxHeight: State<Int>,
|
maxHeight: State<Int>,
|
||||||
scope: CoroutineScope,
|
scope: CoroutineScope,
|
||||||
|
@ -1840,8 +1852,13 @@ private fun scrollToItem(
|
||||||
index = mergedItems.value.indexInParentItems[itemId] ?: -1
|
index = mergedItems.value.indexInParentItems[itemId] ?: -1
|
||||||
}
|
}
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
withContext(scope.coroutineContext) {
|
if (listState.value.layoutInfo.visibleItemsInfo.any { it.index == index && it.offset + it.size <= maxHeight.value }) {
|
||||||
listState.value.animateScrollToItem(min(reversedChatItems.value.lastIndex, index + 1), -maxHeight.value)
|
highlightedItems.value = setOf(itemId)
|
||||||
|
} else {
|
||||||
|
withContext(scope.coroutineContext) {
|
||||||
|
listState.value.animateScrollToItem(min(reversedChatItems.value.lastIndex, index + 1), -maxHeight.value)
|
||||||
|
highlightedItems.value = setOf(itemId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -2,6 +2,8 @@ package chat.simplex.common.views.chat.item
|
||||||
|
|
||||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.*
|
||||||
|
import androidx.compose.foundation.interaction.HoverInteraction
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.shape.*
|
import androidx.compose.foundation.shape.*
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
|
@ -57,6 +59,7 @@ fun ChatItemView(
|
||||||
useLinkPreviews: Boolean,
|
useLinkPreviews: Boolean,
|
||||||
linkMode: SimplexLinkMode,
|
linkMode: SimplexLinkMode,
|
||||||
revealed: State<Boolean>,
|
revealed: State<Boolean>,
|
||||||
|
highlighted: State<Boolean>,
|
||||||
range: State<IntRange?>,
|
range: State<IntRange?>,
|
||||||
selectedChatItems: MutableState<Set<Long>?>,
|
selectedChatItems: MutableState<Set<Long>?>,
|
||||||
fillMaxWidth: Boolean = true,
|
fillMaxWidth: Boolean = true,
|
||||||
|
@ -135,10 +138,19 @@ fun ChatItemView(
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(horizontalAlignment = if (cItem.chatDir.sent) Alignment.End else Alignment.Start) {
|
Column(horizontalAlignment = if (cItem.chatDir.sent) Alignment.End else Alignment.Start) {
|
||||||
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
val enterInteraction = remember { HoverInteraction.Enter() }
|
||||||
|
KeyChangeEffect(highlighted.value) {
|
||||||
|
if (highlighted.value) {
|
||||||
|
interactionSource.emit(enterInteraction)
|
||||||
|
} else {
|
||||||
|
interactionSource.emit(HoverInteraction.Exit(enterInteraction))
|
||||||
|
}
|
||||||
|
}
|
||||||
Column(
|
Column(
|
||||||
Modifier
|
Modifier
|
||||||
.clipChatItem(cItem, itemSeparation.largeGap, revealed.value)
|
.clipChatItem(cItem, itemSeparation.largeGap, revealed.value)
|
||||||
.combinedClickable(onLongClick = { showMenu.value = true }, onClick = onClick)
|
.combinedClickable(onLongClick = { showMenu.value = true }, onClick = onClick, interactionSource = interactionSource, indication = LocalIndication.current)
|
||||||
.onRightClick { showMenu.value = true },
|
.onRightClick { showMenu.value = true },
|
||||||
) {
|
) {
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -1064,6 +1076,7 @@ fun PreviewChatItemView(
|
||||||
linkMode = SimplexLinkMode.DESCRIPTION,
|
linkMode = SimplexLinkMode.DESCRIPTION,
|
||||||
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = true)) },
|
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = true)) },
|
||||||
revealed = remember { mutableStateOf(false) },
|
revealed = remember { mutableStateOf(false) },
|
||||||
|
highlighted = remember { mutableStateOf(false) },
|
||||||
range = remember { mutableStateOf(0..1) },
|
range = remember { mutableStateOf(0..1) },
|
||||||
selectedChatItems = remember { mutableStateOf(setOf()) },
|
selectedChatItems = remember { mutableStateOf(setOf()) },
|
||||||
selectChatItem = {},
|
selectChatItem = {},
|
||||||
|
@ -1106,6 +1119,7 @@ fun PreviewChatItemViewDeletedContent() {
|
||||||
linkMode = SimplexLinkMode.DESCRIPTION,
|
linkMode = SimplexLinkMode.DESCRIPTION,
|
||||||
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = true)) },
|
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = true)) },
|
||||||
revealed = remember { mutableStateOf(false) },
|
revealed = remember { mutableStateOf(false) },
|
||||||
|
highlighted = remember { mutableStateOf(false) },
|
||||||
range = remember { mutableStateOf(0..1) },
|
range = remember { mutableStateOf(0..1) },
|
||||||
selectedChatItems = remember { mutableStateOf(setOf()) },
|
selectedChatItems = remember { mutableStateOf(setOf()) },
|
||||||
selectChatItem = {},
|
selectChatItem = {},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue