From b9239dbbcd261146b1d49277e38feb23331c476d Mon Sep 17 00:00:00 2001 From: Andro_Dev Date: Thu, 16 Apr 2026 20:52:09 +0300 Subject: [PATCH 01/12] fix(chat): full screen images order --- .../chatContent/ChatContentViewers.kt | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentViewers.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentViewers.kt index a345eef7..dcdbfb29 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentViewers.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentViewers.kt @@ -149,9 +149,10 @@ private fun ImagesOverlay( } val viewerItems = remember(images, state.fullScreenImageMessageIds, state.messages) { - if (state.fullScreenImageMessageIds.size == images.size) { + val messageMap = state.messages.associateBy { it.id } + val items = if (state.fullScreenImageMessageIds.size == images.size) { state.fullScreenImageMessageIds.mapIndexed { index, messageId -> - val message = state.messages.firstOrNull { it.id == messageId } + val message = messageMap[messageId] val resolvedPath = message?.displayMediaPathForViewer() ?: images[index] ViewerMediaItem(messageId = messageId, path = resolvedPath) } @@ -164,16 +165,24 @@ private fun ImagesOverlay( ) } } + items.sortedBy { it.messageId } } val viewerImages = remember(viewerItems) { viewerItems.map { it.path } } val imageMessageIds = remember(viewerItems) { viewerItems.map { it.messageId } } - var currentImageIndex by remember(viewerImages, state.fullScreenStartIndex) { + val startMessageId = state.fullScreenImageMessageIds.getOrNull(state.fullScreenStartIndex) + + val startIndex = remember(viewerItems, startMessageId) { + val index = viewerItems.indexOfFirst { it.messageId == startMessageId } + index + .takeIf { it != -1 } + ?.coerceIn(0, viewerItems.lastIndex.coerceAtLeast(0)) + ?: 0 + } + + var currentImageIndex by remember(viewerItems, startIndex) { mutableIntStateOf( - state.fullScreenStartIndex.coerceIn( - 0, - (viewerImages.lastIndex).coerceAtLeast(0) - ) + startIndex.coerceIn(0, viewerItems.lastIndex.coerceAtLeast(0)) ) } @@ -206,7 +215,7 @@ private fun ImagesOverlay( if (viewerImages.isNotEmpty()) { ImageViewer( images = viewerImages, - startIndex = state.fullScreenStartIndex.coerceIn(0, viewerImages.lastIndex), + startIndex = startIndex.coerceIn(0, viewerImages.lastIndex.coerceAtLeast(0)), onDismiss = component::onDismissImages, autoDownload = autoDownload, onPageChanged = { index -> From a7f315470f35d5c0cad578bbb4e1797203c25538 Mon Sep 17 00:00:00 2001 From: andr0d1v Date: Fri, 17 Apr 2026 13:17:09 +0300 Subject: [PATCH 02/12] feat: selection mode on message long press --- .../chats/currentChat/chatContent/ChatContentList.kt | 5 +++++ .../components/AlbumMessageBubbleContainer.kt | 9 ++------- .../components/ChannelMessageBubbleContainer.kt | 5 ++--- .../currentChat/components/MessageBubbleContainer.kt | 9 ++------- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt index 4f0ce731..1c9180e9 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt @@ -605,6 +605,7 @@ private fun MessageBubbleSwitcher( click ) }, + onLongReplyClick = { component.onToggleMessageSelection(item.message.id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> if (isSelectionMode) component.onToggleMessageSelection(id) else component.onSendReaction( @@ -713,6 +714,7 @@ private fun MessageBubbleSwitcher( click ) }, + onLongReplyClick = { component.onToggleMessageSelection(item.message.id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> if (isSelectionMode) component.onToggleMessageSelection(id) else component.onSendReaction( @@ -816,6 +818,7 @@ private fun MessageBubbleSwitcher( click ) }, + onLongReplyClick = { component.onToggleMessageSelection(item.message.last().id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> if (isSelectionMode) component.onToggleMessageSelection(id) else component.onSendReaction( @@ -895,6 +898,7 @@ private fun RootMessageSection( onAudioClick = onAudioClick, onCancelDownload = { component.onCancelDownloadFile(it) }, onReplyClick = { pos, size, click -> onMessageOptionsClick(root, pos, size, click) }, + onLongReplyClick = { component.onToggleMessageSelection(root.id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> component.onSendReaction(id, r) }, onReplyMarkupButtonClick = { id, btn -> component.onReplyMarkupButtonClick(id, btn, root.senderId) }, @@ -932,6 +936,7 @@ private fun RootMessageSection( onAudioClick = onAudioClick, onCancelDownload = { component.onCancelDownloadFile(it) }, onReplyClick = { pos, size, click -> onMessageOptionsClick(root, pos, size, click) }, + onLongReplyClick = { component.onToggleMessageSelection(root.id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> component.onSendReaction(id, r) }, onReplyMarkupButtonClick = { id, btn -> component.onReplyMarkupButtonClick(id, btn, root.senderId) }, diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt index 28104ca8..c9a5d418 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt @@ -63,6 +63,7 @@ fun AlbumMessageBubbleContainer( onAudioClick: (MessageModel) -> Unit = {}, onCancelDownload: (Int) -> Unit = {}, onReplyClick: (Offset, IntSize, Offset) -> Unit, + onLongReplyClick: () -> Unit = {}, onGoToReply: (MessageModel) -> Unit = {}, onReactionClick: (Long, String) -> Unit = { _, _ -> }, onReplyMarkupButtonClick: (Long, InlineKeyboardButtonModel) -> Unit = { _, _ -> }, @@ -172,13 +173,7 @@ fun AlbumMessageBubbleContainer( onReplyClick(bubblePosition, bubbleSize, clickPos) } }, - onLongPress = { offset -> - val clickPos = outerColumnPosition + offset - val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) - if (!bubbleRect.contains(clickPos)) { - onReplyClick(bubblePosition, bubbleSize, clickPos) - } - } + onLongPress = { _ -> onLongReplyClick() } ) } ) { diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/ChannelMessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/ChannelMessageBubbleContainer.kt index bba82aed..133ff67c 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/ChannelMessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/ChannelMessageBubbleContainer.kt @@ -46,6 +46,7 @@ fun ChannelMessageBubbleContainer( onDocumentClick: (MessageModel) -> Unit = {}, onAudioClick: (MessageModel) -> Unit = {}, onReplyClick: (Offset, IntSize, Offset) -> Unit, + onLongReplyClick: () -> Unit = {}, onGoToReply: (MessageModel) -> Unit = {}, autoDownloadMobile: Boolean = false, autoDownloadWifi: Boolean = false, @@ -121,9 +122,7 @@ fun ChannelMessageBubbleContainer( onTap = { offset -> onReplyClick(bubblePosition, bubbleSize, bubblePosition + offset) }, - onLongPress = { offset -> - onReplyClick(bubblePosition, bubbleSize, bubblePosition + offset) - } + onLongPress = { _ -> onLongReplyClick() } ) } diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt index bd774a8a..876d2f65 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt @@ -89,6 +89,7 @@ fun MessageBubbleContainer( onAudioClick: (MessageModel) -> Unit = {}, onCancelDownload: (Int) -> Unit = {}, onReplyClick: (Offset, IntSize, Offset) -> Unit, + onLongReplyClick: () -> Unit = {}, onGoToReply: (MessageModel) -> Unit = {}, onReactionClick: (Long, String) -> Unit = { _, _ -> }, onStickerClick: (Long) -> Unit = {}, @@ -200,13 +201,7 @@ fun MessageBubbleContainer( onReplyClick(bubblePosition, bubbleSize, clickPos) } }, - onLongPress = { offset -> - val clickPos = outerColumnPosition + offset - val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) - if (!bubbleRect.contains(clickPos)) { - onReplyClick(bubblePosition, bubbleSize, clickPos) - } - } + onLongPress = { _ -> onLongReplyClick() } ) } ) { From 959858d2fce2f078a79024ee15c1009a589f7a78 Mon Sep 17 00:00:00 2001 From: Andro_Dev Date: Fri, 17 Apr 2026 14:10:22 +0300 Subject: [PATCH 03/12] fix compile --- .../chatContent/ChatContentList.kt | 2 +- .../ChannelMessageBubbleContainer.kt | 5 +- .../components/MessageBubbleContainer.kt | 102 +++--------------- .../channels/ChannelMessageBubbleContainer.kt | 9 +- 4 files changed, 21 insertions(+), 97 deletions(-) diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt index 1c9180e9..235e615e 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt @@ -818,7 +818,7 @@ private fun MessageBubbleSwitcher( click ) }, - onLongReplyClick = { component.onToggleMessageSelection(item.message.last().id) }, + onLongReplyClick = { component.onToggleMessageSelection(item.messages.last().id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> if (isSelectionMode) component.onToggleMessageSelection(id) else component.onSendReaction( diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/ChannelMessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/ChannelMessageBubbleContainer.kt index 133ff67c..bba82aed 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/ChannelMessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/ChannelMessageBubbleContainer.kt @@ -46,7 +46,6 @@ fun ChannelMessageBubbleContainer( onDocumentClick: (MessageModel) -> Unit = {}, onAudioClick: (MessageModel) -> Unit = {}, onReplyClick: (Offset, IntSize, Offset) -> Unit, - onLongReplyClick: () -> Unit = {}, onGoToReply: (MessageModel) -> Unit = {}, autoDownloadMobile: Boolean = false, autoDownloadWifi: Boolean = false, @@ -122,7 +121,9 @@ fun ChannelMessageBubbleContainer( onTap = { offset -> onReplyClick(bubblePosition, bubbleSize, bubblePosition + offset) }, - onLongPress = { _ -> onLongReplyClick() } + onLongPress = { offset -> + onReplyClick(bubblePosition, bubbleSize, bubblePosition + offset) + } ) } diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt index 876d2f65..6eecc6e8 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt @@ -259,6 +259,7 @@ fun MessageBubbleContainer( onAudioClick = onAudioClick, onCancelDownload = onCancelDownload, onReplyClick = onReplyClick, + onLongReplyClick = onLongReplyClick, onGoToReply = onGoToReply, onReactionClick = onReactionClick, onStickerClick = onStickerClick, @@ -350,6 +351,7 @@ private fun MessageContentSelector( onAudioClick: (MessageModel) -> Unit, onCancelDownload: (Int) -> Unit, onReplyClick: (Offset, IntSize, Offset) -> Unit, + onLongReplyClick: () -> Unit, onGoToReply: (MessageModel) -> Unit, onReactionClick: (Long, String) -> Unit, onStickerClick: (Long) -> Unit, @@ -389,9 +391,7 @@ private fun MessageContentSelector( onClick = { offset -> onReplyClick(bubblePosition, bubbleSize, bubblePosition + offset) }, - onLongClick = { offset -> - onReplyClick(bubblePosition, bubbleSize, bubblePosition + offset) - }, + onLongClick = { onLongReplyClick() }, toProfile = toProfile ) } @@ -405,13 +405,7 @@ private fun MessageContentSelector( onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, onStickerClick = { onStickerClick(it) }, - onLongClick = { - onReplyClick( - bubblePosition, - bubbleSize, - bubblePosition + (bubbleSize.toSize() / 2f).toOffset() - ) - } + onLongClick = { onLongReplyClick() }, ) } @@ -431,13 +425,7 @@ private fun MessageContentSelector( onPhotoClick = onPhotoClick, onDownloadPhoto = onDownloadPhoto, onCancelDownload = onCancelDownload, - onLongClick = { offset -> - onReplyClick( - bubblePosition, - bubbleSize, - bubblePosition + offset - ) - }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -461,13 +449,7 @@ private fun MessageContentSelector( autoplayVideos = autoplayVideos, onVideoClick = onVideoClick, onCancelDownload = onCancelDownload, - onLongClick = { offset -> - onReplyClick( - bubblePosition, - bubbleSize, - bubblePosition + offset - ) - }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -484,13 +466,7 @@ private fun MessageContentSelector( isOutgoing = isOutgoing, onVideoClick = onVideoClick, onCancelDownload = onCancelDownload, - onLongClick = { offset -> - onReplyClick( - bubblePosition, - bubbleSize, - bubblePosition + offset - ) - }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) } ) @@ -512,13 +488,7 @@ private fun MessageContentSelector( autoDownloadRoaming = autoDownloadRoaming, onVoiceClick = onAudioClick, onCancelDownload = onCancelDownload, - onLongClick = { offset -> - onReplyClick( - bubblePosition, - bubbleSize, - bubblePosition + offset - ) - }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -541,13 +511,7 @@ private fun MessageContentSelector( autoplayGifs = autoplayGifs, onGifClick = onVideoClick, onCancelDownload = onCancelDownload, - onLongClick = { offset -> - onReplyClick( - bubblePosition, - bubbleSize, - bubblePosition + offset - ) - }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -572,13 +536,7 @@ private fun MessageContentSelector( autoDownloadRoaming = autoDownloadRoaming, onDocumentClick = onDocumentClick, onCancelDownload = onCancelDownload, - onLongClick = { offset -> - onReplyClick( - bubblePosition, - bubbleSize, - bubblePosition + offset - ) - }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, downloadUtils = downloadUtils @@ -601,13 +559,7 @@ private fun MessageContentSelector( autoDownloadRoaming = autoDownloadRoaming, onAudioClick = onAudioClick, onCancelDownload = onCancelDownload, - onLongClick = { offset -> - onReplyClick( - bubblePosition, - bubbleSize, - bubblePosition + offset - ) - }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, downloadUtils = downloadUtils @@ -626,13 +578,7 @@ private fun MessageContentSelector( bubbleRadius = bubbleRadius, isGroup = isGroup, onClick = { onGoToReply(msg) }, - onLongClick = { - onReplyClick( - bubblePosition, - bubbleSize, - bubblePosition + (bubbleSize.toSize() / 2f).toOffset() - ) - }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -652,13 +598,7 @@ private fun MessageContentSelector( bubbleRadius = bubbleRadius, onOptionClick = { onPollOptionClick(msg.id, it) }, onRetractVote = { onRetractVote(msg.id) }, - onLongClick = { offset -> - onReplyClick( - bubblePosition, - bubbleSize, - bubblePosition + offset - ) - }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, onShowVoters = { onShowVoters(msg.id, it) }, @@ -679,13 +619,7 @@ private fun MessageContentSelector( isGroup = isGroup, bubbleRadius = bubbleRadius, onClick = { onGoToReply(msg) }, - onLongClick = { - onReplyClick( - bubblePosition, - bubbleSize, - bubblePosition + (bubbleSize.toSize() / 2f).toOffset() - ) - }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile @@ -704,13 +638,7 @@ private fun MessageContentSelector( isGroup = isGroup, bubbleRadius = bubbleRadius, onClick = { onGoToReply(msg) }, - onLongClick = { - onReplyClick( - bubblePosition, - bubbleSize, - bubblePosition + (bubbleSize.toSize() / 2f).toOffset() - ) - }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt index 79bae263..63f5ea51 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt @@ -50,6 +50,7 @@ fun ChannelMessageBubbleContainer( onAudioClick: (MessageModel) -> Unit = {}, onCancelDownload: (Int) -> Unit = {}, onReplyClick: (Offset, IntSize, Offset) -> Unit, + onLongReplyClick: () -> Unit = {}, onGoToReply: (MessageModel) -> Unit = {}, autoDownloadMobile: Boolean = false, autoDownloadWifi: Boolean = false, @@ -134,13 +135,7 @@ fun ChannelMessageBubbleContainer( onReplyClick(bubblePosition, bubbleSize, clickPos) } }, - onLongPress = { offset -> - val clickPos = outerColumnPosition + offset - val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) - if (!bubbleRect.contains(clickPos)) { - onReplyClick(bubblePosition, bubbleSize, clickPos) - } - } + onLongPress = { _ -> onLongReplyClick() } ) } ) { From c6b711ce8ade041ab18f0c61fc411a3154665e18 Mon Sep 17 00:00:00 2001 From: Andro_Dev Date: Fri, 17 Apr 2026 15:42:23 +0300 Subject: [PATCH 04/12] fix: selection --- .../chatContent/ChatContentList.kt | 36 +++++++++----- .../components/AlbumMessageBubbleContainer.kt | 19 ++++--- .../components/MessageBubbleContainer.kt | 49 ++++++++++--------- .../channels/ChannelMessageBubbleContainer.kt | 17 ++++--- 4 files changed, 73 insertions(+), 48 deletions(-) diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt index 235e615e..b3616bae 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt @@ -49,6 +49,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableLongStateOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue @@ -226,6 +227,8 @@ fun ChatContentList( } } + val isSelectionMode = mutableStateOf(state.selectedMessageIds.isNotEmpty()) + if (isComments) { item(key = "root_header") { RootMessageSection( @@ -241,7 +244,8 @@ fun ChatContentList( onViaBotClick, toProfile, downloadUtils, - isAnyViewerOpen = isAnyViewerOpen + isAnyViewerOpen = isAnyViewerOpen, + isSelectionMode = isSelectionMode.value ) } @@ -270,7 +274,7 @@ fun ChatContentList( olderMsg = olderMsg, newerMsg = newerMsg, isSelected = isItemSelected(item, state.selectedMessageIds), - isSelectionMode = state.selectedMessageIds.isNotEmpty(), + isSelectionMode = isSelectionMode.value, selectedMessageId = selectedMessageId, onPhotoClick = onPhotoClick, onPhotoDownload = onPhotoDownload, @@ -605,7 +609,7 @@ private fun MessageBubbleSwitcher( click ) }, - onLongReplyClick = { component.onToggleMessageSelection(item.message.id) }, + onSelectClick = { component.onToggleMessageSelection(item.message.id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> if (isSelectionMode) component.onToggleMessageSelection(id) else component.onSendReaction( @@ -661,7 +665,8 @@ private fun MessageBubbleSwitcher( onYouTubeClick = { component.onOpenYouTube(it) }, onInstantViewClick = { component.onOpenInstantView(it) }, downloadUtils = downloadUtils, - isAnyViewerOpen = isAnyViewerOpen + isAnyViewerOpen = isAnyViewerOpen, + isSelectionMode = isSelectionMode, ) } else { MessageBubbleContainer( @@ -714,7 +719,7 @@ private fun MessageBubbleSwitcher( click ) }, - onLongReplyClick = { component.onToggleMessageSelection(item.message.id) }, + onSelectClick = { component.onToggleMessageSelection(item.message.id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> if (isSelectionMode) component.onToggleMessageSelection(id) else component.onSendReaction( @@ -766,7 +771,8 @@ private fun MessageBubbleSwitcher( onReplySwipe = { component.onReplyMessage(it) }, swipeEnabled = !isSelectionMode, downloadUtils = downloadUtils, - isAnyViewerOpen = isAnyViewerOpen + isAnyViewerOpen = isAnyViewerOpen, + isSelectionMode = isSelectionMode, ) } } @@ -818,7 +824,7 @@ private fun MessageBubbleSwitcher( click ) }, - onLongReplyClick = { component.onToggleMessageSelection(item.messages.last().id) }, + onSelectClick = { component.onToggleMessageSelection(item.messages.last().id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> if (isSelectionMode) component.onToggleMessageSelection(id) else component.onSendReaction( @@ -835,7 +841,8 @@ private fun MessageBubbleSwitcher( onReplySwipe = { component.onReplyMessage(it) }, swipeEnabled = !isSelectionMode, downloadUtils = downloadUtils, - isAnyViewerOpen = isAnyViewerOpen + isAnyViewerOpen = isAnyViewerOpen, + isSelectionMode = isSelectionMode, ) } } @@ -878,7 +885,8 @@ private fun RootMessageSection( onViaBotClick: (String) -> Unit, toProfile: (Long) -> Unit, downloadUtils: IDownloadUtils, - isAnyViewerOpen: Boolean = false + isAnyViewerOpen: Boolean = false, + isSelectionMode: Boolean = false, ) { val root = state.rootMessage ?: return Column( @@ -898,7 +906,7 @@ private fun RootMessageSection( onAudioClick = onAudioClick, onCancelDownload = { component.onCancelDownloadFile(it) }, onReplyClick = { pos, size, click -> onMessageOptionsClick(root, pos, size, click) }, - onLongReplyClick = { component.onToggleMessageSelection(root.id) }, + onSelectClick = { component.onToggleMessageSelection(root.id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> component.onSendReaction(id, r) }, onReplyMarkupButtonClick = { id, btn -> component.onReplyMarkupButtonClick(id, btn, root.senderId) }, @@ -917,7 +925,8 @@ private fun RootMessageSection( onYouTubeClick = { component.onOpenYouTube(it) }, onInstantViewClick = { component.onOpenInstantView(it) }, downloadUtils = downloadUtils, - isAnyViewerOpen = isAnyViewerOpen + isAnyViewerOpen = isAnyViewerOpen, + isSelectionMode = isSelectionMode, ) } else { MessageBubbleContainer( @@ -936,7 +945,7 @@ private fun RootMessageSection( onAudioClick = onAudioClick, onCancelDownload = { component.onCancelDownloadFile(it) }, onReplyClick = { pos, size, click -> onMessageOptionsClick(root, pos, size, click) }, - onLongReplyClick = { component.onToggleMessageSelection(root.id) }, + onSelectClick = { component.onToggleMessageSelection(root.id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> component.onSendReaction(id, r) }, onReplyMarkupButtonClick = { id, btn -> component.onReplyMarkupButtonClick(id, btn, root.senderId) }, @@ -951,7 +960,8 @@ private fun RootMessageSection( onInstantViewClick = { component.onOpenInstantView(it) }, onYouTubeClick = { component.onOpenYouTube(it) }, downloadUtils = downloadUtils, - isAnyViewerOpen = isAnyViewerOpen + isAnyViewerOpen = isAnyViewerOpen, + isSelectionMode = isSelectionMode, ) } diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt index c9a5d418..7cba6035 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt @@ -63,7 +63,7 @@ fun AlbumMessageBubbleContainer( onAudioClick: (MessageModel) -> Unit = {}, onCancelDownload: (Int) -> Unit = {}, onReplyClick: (Offset, IntSize, Offset) -> Unit, - onLongReplyClick: () -> Unit = {}, + onSelectClick: () -> Unit = {}, onGoToReply: (MessageModel) -> Unit = {}, onReactionClick: (Long, String) -> Unit = { _, _ -> }, onReplyMarkupButtonClick: (Long, InlineKeyboardButtonModel) -> Unit = { _, _ -> }, @@ -79,7 +79,8 @@ fun AlbumMessageBubbleContainer( onReplySwipe: (MessageModel) -> Unit = {}, swipeEnabled: Boolean = true, downloadUtils: IDownloadUtils, - isAnyViewerOpen: Boolean = false + isAnyViewerOpen: Boolean = false, + isSelectionMode: Boolean = false, ) { if (messages.isEmpty()) return @@ -167,13 +168,17 @@ fun AlbumMessageBubbleContainer( .pointerInput(Unit) { detectTapGestures( onTap = { offset -> - val clickPos = outerColumnPosition + offset - val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) - if (!bubbleRect.contains(clickPos)) { - onReplyClick(bubblePosition, bubbleSize, clickPos) + if (isSelectionMode) { + onSelectClick() + } else { + val clickPos = outerColumnPosition + offset + val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) + if (!bubbleRect.contains(clickPos)) { + onReplyClick(bubblePosition, bubbleSize, clickPos) + } } }, - onLongPress = { _ -> onLongReplyClick() } + onLongPress = { _ -> onSelectClick() } ) } ) { diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt index 6eecc6e8..6750c00b 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt @@ -89,7 +89,7 @@ fun MessageBubbleContainer( onAudioClick: (MessageModel) -> Unit = {}, onCancelDownload: (Int) -> Unit = {}, onReplyClick: (Offset, IntSize, Offset) -> Unit, - onLongReplyClick: () -> Unit = {}, + onSelectClick: () -> Unit = {}, onGoToReply: (MessageModel) -> Unit = {}, onReactionClick: (Long, String) -> Unit = { _, _ -> }, onStickerClick: (Long) -> Unit = {}, @@ -108,7 +108,8 @@ fun MessageBubbleContainer( onReplySwipe: (MessageModel) -> Unit = {}, swipeEnabled: Boolean = true, downloadUtils: IDownloadUtils, - isAnyViewerOpen: Boolean = false + isAnyViewerOpen: Boolean = false, + isSelectionMode: Boolean = false, ) { val configuration = LocalConfiguration.current val screenWidth = configuration.screenWidthDp.dp @@ -195,13 +196,17 @@ fun MessageBubbleContainer( .pointerInput(Unit) { detectTapGestures( onTap = { offset -> - val clickPos = outerColumnPosition + offset - val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) - if (!bubbleRect.contains(clickPos)) { - onReplyClick(bubblePosition, bubbleSize, clickPos) + if (isSelectionMode) { + onSelectClick() + } else { + val clickPos = outerColumnPosition + offset + val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) + if (!bubbleRect.contains(clickPos)) { + onReplyClick(bubblePosition, bubbleSize, clickPos) + } } }, - onLongPress = { _ -> onLongReplyClick() } + onLongPress = { onSelectClick() } ) } ) { @@ -259,7 +264,7 @@ fun MessageBubbleContainer( onAudioClick = onAudioClick, onCancelDownload = onCancelDownload, onReplyClick = onReplyClick, - onLongReplyClick = onLongReplyClick, + onSelectClick = onSelectClick, onGoToReply = onGoToReply, onReactionClick = onReactionClick, onStickerClick = onStickerClick, @@ -351,7 +356,7 @@ private fun MessageContentSelector( onAudioClick: (MessageModel) -> Unit, onCancelDownload: (Int) -> Unit, onReplyClick: (Offset, IntSize, Offset) -> Unit, - onLongReplyClick: () -> Unit, + onSelectClick: () -> Unit, onGoToReply: (MessageModel) -> Unit, onReactionClick: (Long, String) -> Unit, onStickerClick: (Long) -> Unit, @@ -391,7 +396,7 @@ private fun MessageContentSelector( onClick = { offset -> onReplyClick(bubblePosition, bubbleSize, bubblePosition + offset) }, - onLongClick = { onLongReplyClick() }, + onLongClick = { onSelectClick() }, toProfile = toProfile ) } @@ -405,7 +410,7 @@ private fun MessageContentSelector( onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, onStickerClick = { onStickerClick(it) }, - onLongClick = { onLongReplyClick() }, + onLongClick = { onSelectClick() }, ) } @@ -425,7 +430,7 @@ private fun MessageContentSelector( onPhotoClick = onPhotoClick, onDownloadPhoto = onDownloadPhoto, onCancelDownload = onCancelDownload, - onLongClick = { onLongReplyClick() }, + onLongClick = { onSelectClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -449,7 +454,7 @@ private fun MessageContentSelector( autoplayVideos = autoplayVideos, onVideoClick = onVideoClick, onCancelDownload = onCancelDownload, - onLongClick = { onLongReplyClick() }, + onLongClick = { onSelectClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -466,7 +471,7 @@ private fun MessageContentSelector( isOutgoing = isOutgoing, onVideoClick = onVideoClick, onCancelDownload = onCancelDownload, - onLongClick = { onLongReplyClick() }, + onLongClick = { onSelectClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) } ) @@ -488,7 +493,7 @@ private fun MessageContentSelector( autoDownloadRoaming = autoDownloadRoaming, onVoiceClick = onAudioClick, onCancelDownload = onCancelDownload, - onLongClick = { onLongReplyClick() }, + onLongClick = { onSelectClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -511,7 +516,7 @@ private fun MessageContentSelector( autoplayGifs = autoplayGifs, onGifClick = onVideoClick, onCancelDownload = onCancelDownload, - onLongClick = { onLongReplyClick() }, + onLongClick = { onSelectClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -536,7 +541,7 @@ private fun MessageContentSelector( autoDownloadRoaming = autoDownloadRoaming, onDocumentClick = onDocumentClick, onCancelDownload = onCancelDownload, - onLongClick = { onLongReplyClick() }, + onLongClick = { onSelectClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, downloadUtils = downloadUtils @@ -559,7 +564,7 @@ private fun MessageContentSelector( autoDownloadRoaming = autoDownloadRoaming, onAudioClick = onAudioClick, onCancelDownload = onCancelDownload, - onLongClick = { onLongReplyClick() }, + onLongClick = { onSelectClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, downloadUtils = downloadUtils @@ -578,7 +583,7 @@ private fun MessageContentSelector( bubbleRadius = bubbleRadius, isGroup = isGroup, onClick = { onGoToReply(msg) }, - onLongClick = { onLongReplyClick() }, + onLongClick = { onSelectClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -598,7 +603,7 @@ private fun MessageContentSelector( bubbleRadius = bubbleRadius, onOptionClick = { onPollOptionClick(msg.id, it) }, onRetractVote = { onRetractVote(msg.id) }, - onLongClick = { onLongReplyClick() }, + onLongClick = { onSelectClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, onShowVoters = { onShowVoters(msg.id, it) }, @@ -619,7 +624,7 @@ private fun MessageContentSelector( isGroup = isGroup, bubbleRadius = bubbleRadius, onClick = { onGoToReply(msg) }, - onLongClick = { onLongReplyClick() }, + onLongClick = { onSelectClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile @@ -638,7 +643,7 @@ private fun MessageContentSelector( isGroup = isGroup, bubbleRadius = bubbleRadius, onClick = { onGoToReply(msg) }, - onLongClick = { onLongReplyClick() }, + onLongClick = { onSelectClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt index 63f5ea51..531ffdf6 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt @@ -50,7 +50,7 @@ fun ChannelMessageBubbleContainer( onAudioClick: (MessageModel) -> Unit = {}, onCancelDownload: (Int) -> Unit = {}, onReplyClick: (Offset, IntSize, Offset) -> Unit, - onLongReplyClick: () -> Unit = {}, + onSelectClick: () -> Unit = {}, onGoToReply: (MessageModel) -> Unit = {}, autoDownloadMobile: Boolean = false, autoDownloadWifi: Boolean = false, @@ -78,6 +78,7 @@ fun ChannelMessageBubbleContainer( onReplySwipe: (MessageModel) -> Unit = {}, downloadUtils: IDownloadUtils, isAnyViewerOpen: Boolean = false, + isSelectionMode: Boolean = false, ) { val configuration = LocalConfiguration.current val screenWidth = configuration.screenWidthDp.dp @@ -129,13 +130,17 @@ fun ChannelMessageBubbleContainer( .pointerInput(Unit) { detectTapGestures( onTap = { offset -> - val clickPos = outerColumnPosition + offset - val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) - if (!bubbleRect.contains(clickPos)) { - onReplyClick(bubblePosition, bubbleSize, clickPos) + if (isSelectionMode) { + onSelectClick() + } else { + val clickPos = outerColumnPosition + offset + val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) + if (!bubbleRect.contains(clickPos)) { + onReplyClick(bubblePosition, bubbleSize, clickPos) + } } }, - onLongPress = { _ -> onLongReplyClick() } + onLongPress = { _ -> onSelectClick() } ) } ) { From 2061c332946f98fac8cfe3133cc8b52bd55cf3b2 Mon Sep 17 00:00:00 2001 From: Andro_Dev Date: Sat, 18 Apr 2026 09:18:24 +0300 Subject: [PATCH 05/12] fix: selection --- .../currentChat/chatContent/ChatContentList.kt | 6 ++---- .../components/AlbumMessageBubbleContainer.kt | 16 ++-------------- .../components/MessageBubbleContainer.kt | 17 ++++++++++++++--- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt index b3616bae..26fbe446 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt @@ -227,8 +227,6 @@ fun ChatContentList( } } - val isSelectionMode = mutableStateOf(state.selectedMessageIds.isNotEmpty()) - if (isComments) { item(key = "root_header") { RootMessageSection( @@ -245,7 +243,7 @@ fun ChatContentList( toProfile, downloadUtils, isAnyViewerOpen = isAnyViewerOpen, - isSelectionMode = isSelectionMode.value + isSelectionMode = state.selectedMessageIds.isNotEmpty() ) } @@ -274,7 +272,7 @@ fun ChatContentList( olderMsg = olderMsg, newerMsg = newerMsg, isSelected = isItemSelected(item, state.selectedMessageIds), - isSelectionMode = isSelectionMode.value, + isSelectionMode = state.selectedMessageIds.isNotEmpty(), selectedMessageId = selectedMessageId, onPhotoClick = onPhotoClick, onPhotoDownload = onPhotoDownload, diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt index 7cba6035..fab72d32 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt @@ -243,13 +243,7 @@ fun AlbumMessageBubbleContainer( onDocumentClick = onDocumentClick, onAudioClick = onAudioClick, onCancelDownload = onCancelDownload, - onLongClick = { offset -> - onReplyClick( - bubblePosition, - bubbleSize, - bubblePosition + offset - ) - }, + onLongClick = { onSelectClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(lastMsg.id, it) }, onCommentsClick = onCommentsClick, @@ -279,13 +273,7 @@ fun AlbumMessageBubbleContainer( onDocumentClick = onDocumentClick, onAudioClick = onAudioClick, onCancelDownload = onCancelDownload, - onLongClick = { offset -> - onReplyClick( - bubblePosition, - bubbleSize, - bubblePosition + offset - ) - }, + onLongClick = { onSelectClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(lastMsg.id, it) }, toProfile = toProfile, diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt index 6750c00b..c362ad1f 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt @@ -5,7 +5,9 @@ import androidx.compose.animation.Animatable import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.tween import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -278,7 +280,8 @@ fun MessageBubbleContainer( bubblePosition = bubblePosition, bubbleSize = bubbleSize, downloadUtils = downloadUtils, - isAnyViewerOpen = isAnyViewerOpen + isAnyViewerOpen = isAnyViewerOpen, + isSelectionMode = isSelectionMode ) MessageReplyMarkup( @@ -370,10 +373,18 @@ private fun MessageContentSelector( bubblePosition: Offset, bubbleSize: IntSize, downloadUtils: IDownloadUtils, - isAnyViewerOpen: Boolean = false + isAnyViewerOpen: Boolean = false, + isSelectionMode: Boolean = false, ) { Column( - modifier = Modifier.width(IntrinsicSize.Max), + modifier = Modifier + .width(IntrinsicSize.Max) + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = null, + enabled = isSelectionMode, + onClick = { onSelectClick() } + ), horizontalAlignment = if (isOutgoing) Alignment.End else Alignment.Start ) { when (val content = msg.content) { From 33c53e1ef624ee16e1e1e5f5d49d552154a91fbe Mon Sep 17 00:00:00 2001 From: Andro_Dev Date: Sat, 18 Apr 2026 14:22:52 +0300 Subject: [PATCH 06/12] Revert "fix: selection" This reverts commit 2061c332946f98fac8cfe3133cc8b52bd55cf3b2. --- .../currentChat/chatContent/ChatContentList.kt | 6 ++++-- .../components/AlbumMessageBubbleContainer.kt | 16 ++++++++++++++-- .../components/MessageBubbleContainer.kt | 17 +++-------------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt index 26fbe446..b3616bae 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt @@ -227,6 +227,8 @@ fun ChatContentList( } } + val isSelectionMode = mutableStateOf(state.selectedMessageIds.isNotEmpty()) + if (isComments) { item(key = "root_header") { RootMessageSection( @@ -243,7 +245,7 @@ fun ChatContentList( toProfile, downloadUtils, isAnyViewerOpen = isAnyViewerOpen, - isSelectionMode = state.selectedMessageIds.isNotEmpty() + isSelectionMode = isSelectionMode.value ) } @@ -272,7 +274,7 @@ fun ChatContentList( olderMsg = olderMsg, newerMsg = newerMsg, isSelected = isItemSelected(item, state.selectedMessageIds), - isSelectionMode = state.selectedMessageIds.isNotEmpty(), + isSelectionMode = isSelectionMode.value, selectedMessageId = selectedMessageId, onPhotoClick = onPhotoClick, onPhotoDownload = onPhotoDownload, diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt index fab72d32..7cba6035 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt @@ -243,7 +243,13 @@ fun AlbumMessageBubbleContainer( onDocumentClick = onDocumentClick, onAudioClick = onAudioClick, onCancelDownload = onCancelDownload, - onLongClick = { onSelectClick() }, + onLongClick = { offset -> + onReplyClick( + bubblePosition, + bubbleSize, + bubblePosition + offset + ) + }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(lastMsg.id, it) }, onCommentsClick = onCommentsClick, @@ -273,7 +279,13 @@ fun AlbumMessageBubbleContainer( onDocumentClick = onDocumentClick, onAudioClick = onAudioClick, onCancelDownload = onCancelDownload, - onLongClick = { onSelectClick() }, + onLongClick = { offset -> + onReplyClick( + bubblePosition, + bubbleSize, + bubblePosition + offset + ) + }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(lastMsg.id, it) }, toProfile = toProfile, diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt index c362ad1f..6750c00b 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt @@ -5,9 +5,7 @@ import androidx.compose.animation.Animatable import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.tween import androidx.compose.foundation.background -import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -280,8 +278,7 @@ fun MessageBubbleContainer( bubblePosition = bubblePosition, bubbleSize = bubbleSize, downloadUtils = downloadUtils, - isAnyViewerOpen = isAnyViewerOpen, - isSelectionMode = isSelectionMode + isAnyViewerOpen = isAnyViewerOpen ) MessageReplyMarkup( @@ -373,18 +370,10 @@ private fun MessageContentSelector( bubblePosition: Offset, bubbleSize: IntSize, downloadUtils: IDownloadUtils, - isAnyViewerOpen: Boolean = false, - isSelectionMode: Boolean = false, + isAnyViewerOpen: Boolean = false ) { Column( - modifier = Modifier - .width(IntrinsicSize.Max) - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = null, - enabled = isSelectionMode, - onClick = { onSelectClick() } - ), + modifier = Modifier.width(IntrinsicSize.Max), horizontalAlignment = if (isOutgoing) Alignment.End else Alignment.Start ) { when (val content = msg.content) { From 3fa17b94c0a439a2974e7cc9cbdf4503d361f5f0 Mon Sep 17 00:00:00 2001 From: Andro_Dev Date: Sat, 18 Apr 2026 14:23:10 +0300 Subject: [PATCH 07/12] Revert "fix: selection" This reverts commit c6b711ce8ade041ab18f0c61fc411a3154665e18. --- .../chatContent/ChatContentList.kt | 36 +++++--------- .../components/AlbumMessageBubbleContainer.kt | 19 +++---- .../components/MessageBubbleContainer.kt | 49 +++++++++---------- .../channels/ChannelMessageBubbleContainer.kt | 17 +++---- 4 files changed, 48 insertions(+), 73 deletions(-) diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt index b3616bae..235e615e 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt @@ -49,7 +49,6 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableLongStateOf -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue @@ -227,8 +226,6 @@ fun ChatContentList( } } - val isSelectionMode = mutableStateOf(state.selectedMessageIds.isNotEmpty()) - if (isComments) { item(key = "root_header") { RootMessageSection( @@ -244,8 +241,7 @@ fun ChatContentList( onViaBotClick, toProfile, downloadUtils, - isAnyViewerOpen = isAnyViewerOpen, - isSelectionMode = isSelectionMode.value + isAnyViewerOpen = isAnyViewerOpen ) } @@ -274,7 +270,7 @@ fun ChatContentList( olderMsg = olderMsg, newerMsg = newerMsg, isSelected = isItemSelected(item, state.selectedMessageIds), - isSelectionMode = isSelectionMode.value, + isSelectionMode = state.selectedMessageIds.isNotEmpty(), selectedMessageId = selectedMessageId, onPhotoClick = onPhotoClick, onPhotoDownload = onPhotoDownload, @@ -609,7 +605,7 @@ private fun MessageBubbleSwitcher( click ) }, - onSelectClick = { component.onToggleMessageSelection(item.message.id) }, + onLongReplyClick = { component.onToggleMessageSelection(item.message.id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> if (isSelectionMode) component.onToggleMessageSelection(id) else component.onSendReaction( @@ -665,8 +661,7 @@ private fun MessageBubbleSwitcher( onYouTubeClick = { component.onOpenYouTube(it) }, onInstantViewClick = { component.onOpenInstantView(it) }, downloadUtils = downloadUtils, - isAnyViewerOpen = isAnyViewerOpen, - isSelectionMode = isSelectionMode, + isAnyViewerOpen = isAnyViewerOpen ) } else { MessageBubbleContainer( @@ -719,7 +714,7 @@ private fun MessageBubbleSwitcher( click ) }, - onSelectClick = { component.onToggleMessageSelection(item.message.id) }, + onLongReplyClick = { component.onToggleMessageSelection(item.message.id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> if (isSelectionMode) component.onToggleMessageSelection(id) else component.onSendReaction( @@ -771,8 +766,7 @@ private fun MessageBubbleSwitcher( onReplySwipe = { component.onReplyMessage(it) }, swipeEnabled = !isSelectionMode, downloadUtils = downloadUtils, - isAnyViewerOpen = isAnyViewerOpen, - isSelectionMode = isSelectionMode, + isAnyViewerOpen = isAnyViewerOpen ) } } @@ -824,7 +818,7 @@ private fun MessageBubbleSwitcher( click ) }, - onSelectClick = { component.onToggleMessageSelection(item.messages.last().id) }, + onLongReplyClick = { component.onToggleMessageSelection(item.messages.last().id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> if (isSelectionMode) component.onToggleMessageSelection(id) else component.onSendReaction( @@ -841,8 +835,7 @@ private fun MessageBubbleSwitcher( onReplySwipe = { component.onReplyMessage(it) }, swipeEnabled = !isSelectionMode, downloadUtils = downloadUtils, - isAnyViewerOpen = isAnyViewerOpen, - isSelectionMode = isSelectionMode, + isAnyViewerOpen = isAnyViewerOpen ) } } @@ -885,8 +878,7 @@ private fun RootMessageSection( onViaBotClick: (String) -> Unit, toProfile: (Long) -> Unit, downloadUtils: IDownloadUtils, - isAnyViewerOpen: Boolean = false, - isSelectionMode: Boolean = false, + isAnyViewerOpen: Boolean = false ) { val root = state.rootMessage ?: return Column( @@ -906,7 +898,7 @@ private fun RootMessageSection( onAudioClick = onAudioClick, onCancelDownload = { component.onCancelDownloadFile(it) }, onReplyClick = { pos, size, click -> onMessageOptionsClick(root, pos, size, click) }, - onSelectClick = { component.onToggleMessageSelection(root.id) }, + onLongReplyClick = { component.onToggleMessageSelection(root.id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> component.onSendReaction(id, r) }, onReplyMarkupButtonClick = { id, btn -> component.onReplyMarkupButtonClick(id, btn, root.senderId) }, @@ -925,8 +917,7 @@ private fun RootMessageSection( onYouTubeClick = { component.onOpenYouTube(it) }, onInstantViewClick = { component.onOpenInstantView(it) }, downloadUtils = downloadUtils, - isAnyViewerOpen = isAnyViewerOpen, - isSelectionMode = isSelectionMode, + isAnyViewerOpen = isAnyViewerOpen ) } else { MessageBubbleContainer( @@ -945,7 +936,7 @@ private fun RootMessageSection( onAudioClick = onAudioClick, onCancelDownload = { component.onCancelDownloadFile(it) }, onReplyClick = { pos, size, click -> onMessageOptionsClick(root, pos, size, click) }, - onSelectClick = { component.onToggleMessageSelection(root.id) }, + onLongReplyClick = { component.onToggleMessageSelection(root.id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> component.onSendReaction(id, r) }, onReplyMarkupButtonClick = { id, btn -> component.onReplyMarkupButtonClick(id, btn, root.senderId) }, @@ -960,8 +951,7 @@ private fun RootMessageSection( onInstantViewClick = { component.onOpenInstantView(it) }, onYouTubeClick = { component.onOpenYouTube(it) }, downloadUtils = downloadUtils, - isAnyViewerOpen = isAnyViewerOpen, - isSelectionMode = isSelectionMode, + isAnyViewerOpen = isAnyViewerOpen ) } diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt index 7cba6035..c9a5d418 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt @@ -63,7 +63,7 @@ fun AlbumMessageBubbleContainer( onAudioClick: (MessageModel) -> Unit = {}, onCancelDownload: (Int) -> Unit = {}, onReplyClick: (Offset, IntSize, Offset) -> Unit, - onSelectClick: () -> Unit = {}, + onLongReplyClick: () -> Unit = {}, onGoToReply: (MessageModel) -> Unit = {}, onReactionClick: (Long, String) -> Unit = { _, _ -> }, onReplyMarkupButtonClick: (Long, InlineKeyboardButtonModel) -> Unit = { _, _ -> }, @@ -79,8 +79,7 @@ fun AlbumMessageBubbleContainer( onReplySwipe: (MessageModel) -> Unit = {}, swipeEnabled: Boolean = true, downloadUtils: IDownloadUtils, - isAnyViewerOpen: Boolean = false, - isSelectionMode: Boolean = false, + isAnyViewerOpen: Boolean = false ) { if (messages.isEmpty()) return @@ -168,17 +167,13 @@ fun AlbumMessageBubbleContainer( .pointerInput(Unit) { detectTapGestures( onTap = { offset -> - if (isSelectionMode) { - onSelectClick() - } else { - val clickPos = outerColumnPosition + offset - val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) - if (!bubbleRect.contains(clickPos)) { - onReplyClick(bubblePosition, bubbleSize, clickPos) - } + val clickPos = outerColumnPosition + offset + val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) + if (!bubbleRect.contains(clickPos)) { + onReplyClick(bubblePosition, bubbleSize, clickPos) } }, - onLongPress = { _ -> onSelectClick() } + onLongPress = { _ -> onLongReplyClick() } ) } ) { diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt index 6750c00b..6eecc6e8 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt @@ -89,7 +89,7 @@ fun MessageBubbleContainer( onAudioClick: (MessageModel) -> Unit = {}, onCancelDownload: (Int) -> Unit = {}, onReplyClick: (Offset, IntSize, Offset) -> Unit, - onSelectClick: () -> Unit = {}, + onLongReplyClick: () -> Unit = {}, onGoToReply: (MessageModel) -> Unit = {}, onReactionClick: (Long, String) -> Unit = { _, _ -> }, onStickerClick: (Long) -> Unit = {}, @@ -108,8 +108,7 @@ fun MessageBubbleContainer( onReplySwipe: (MessageModel) -> Unit = {}, swipeEnabled: Boolean = true, downloadUtils: IDownloadUtils, - isAnyViewerOpen: Boolean = false, - isSelectionMode: Boolean = false, + isAnyViewerOpen: Boolean = false ) { val configuration = LocalConfiguration.current val screenWidth = configuration.screenWidthDp.dp @@ -196,17 +195,13 @@ fun MessageBubbleContainer( .pointerInput(Unit) { detectTapGestures( onTap = { offset -> - if (isSelectionMode) { - onSelectClick() - } else { - val clickPos = outerColumnPosition + offset - val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) - if (!bubbleRect.contains(clickPos)) { - onReplyClick(bubblePosition, bubbleSize, clickPos) - } + val clickPos = outerColumnPosition + offset + val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) + if (!bubbleRect.contains(clickPos)) { + onReplyClick(bubblePosition, bubbleSize, clickPos) } }, - onLongPress = { onSelectClick() } + onLongPress = { _ -> onLongReplyClick() } ) } ) { @@ -264,7 +259,7 @@ fun MessageBubbleContainer( onAudioClick = onAudioClick, onCancelDownload = onCancelDownload, onReplyClick = onReplyClick, - onSelectClick = onSelectClick, + onLongReplyClick = onLongReplyClick, onGoToReply = onGoToReply, onReactionClick = onReactionClick, onStickerClick = onStickerClick, @@ -356,7 +351,7 @@ private fun MessageContentSelector( onAudioClick: (MessageModel) -> Unit, onCancelDownload: (Int) -> Unit, onReplyClick: (Offset, IntSize, Offset) -> Unit, - onSelectClick: () -> Unit, + onLongReplyClick: () -> Unit, onGoToReply: (MessageModel) -> Unit, onReactionClick: (Long, String) -> Unit, onStickerClick: (Long) -> Unit, @@ -396,7 +391,7 @@ private fun MessageContentSelector( onClick = { offset -> onReplyClick(bubblePosition, bubbleSize, bubblePosition + offset) }, - onLongClick = { onSelectClick() }, + onLongClick = { onLongReplyClick() }, toProfile = toProfile ) } @@ -410,7 +405,7 @@ private fun MessageContentSelector( onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, onStickerClick = { onStickerClick(it) }, - onLongClick = { onSelectClick() }, + onLongClick = { onLongReplyClick() }, ) } @@ -430,7 +425,7 @@ private fun MessageContentSelector( onPhotoClick = onPhotoClick, onDownloadPhoto = onDownloadPhoto, onCancelDownload = onCancelDownload, - onLongClick = { onSelectClick() }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -454,7 +449,7 @@ private fun MessageContentSelector( autoplayVideos = autoplayVideos, onVideoClick = onVideoClick, onCancelDownload = onCancelDownload, - onLongClick = { onSelectClick() }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -471,7 +466,7 @@ private fun MessageContentSelector( isOutgoing = isOutgoing, onVideoClick = onVideoClick, onCancelDownload = onCancelDownload, - onLongClick = { onSelectClick() }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) } ) @@ -493,7 +488,7 @@ private fun MessageContentSelector( autoDownloadRoaming = autoDownloadRoaming, onVoiceClick = onAudioClick, onCancelDownload = onCancelDownload, - onLongClick = { onSelectClick() }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -516,7 +511,7 @@ private fun MessageContentSelector( autoplayGifs = autoplayGifs, onGifClick = onVideoClick, onCancelDownload = onCancelDownload, - onLongClick = { onSelectClick() }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -541,7 +536,7 @@ private fun MessageContentSelector( autoDownloadRoaming = autoDownloadRoaming, onDocumentClick = onDocumentClick, onCancelDownload = onCancelDownload, - onLongClick = { onSelectClick() }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, downloadUtils = downloadUtils @@ -564,7 +559,7 @@ private fun MessageContentSelector( autoDownloadRoaming = autoDownloadRoaming, onAudioClick = onAudioClick, onCancelDownload = onCancelDownload, - onLongClick = { onSelectClick() }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, downloadUtils = downloadUtils @@ -583,7 +578,7 @@ private fun MessageContentSelector( bubbleRadius = bubbleRadius, isGroup = isGroup, onClick = { onGoToReply(msg) }, - onLongClick = { onSelectClick() }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -603,7 +598,7 @@ private fun MessageContentSelector( bubbleRadius = bubbleRadius, onOptionClick = { onPollOptionClick(msg.id, it) }, onRetractVote = { onRetractVote(msg.id) }, - onLongClick = { onSelectClick() }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, onShowVoters = { onShowVoters(msg.id, it) }, @@ -624,7 +619,7 @@ private fun MessageContentSelector( isGroup = isGroup, bubbleRadius = bubbleRadius, onClick = { onGoToReply(msg) }, - onLongClick = { onSelectClick() }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile @@ -643,7 +638,7 @@ private fun MessageContentSelector( isGroup = isGroup, bubbleRadius = bubbleRadius, onClick = { onGoToReply(msg) }, - onLongClick = { onSelectClick() }, + onLongClick = { onLongReplyClick() }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt index 531ffdf6..63f5ea51 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt @@ -50,7 +50,7 @@ fun ChannelMessageBubbleContainer( onAudioClick: (MessageModel) -> Unit = {}, onCancelDownload: (Int) -> Unit = {}, onReplyClick: (Offset, IntSize, Offset) -> Unit, - onSelectClick: () -> Unit = {}, + onLongReplyClick: () -> Unit = {}, onGoToReply: (MessageModel) -> Unit = {}, autoDownloadMobile: Boolean = false, autoDownloadWifi: Boolean = false, @@ -78,7 +78,6 @@ fun ChannelMessageBubbleContainer( onReplySwipe: (MessageModel) -> Unit = {}, downloadUtils: IDownloadUtils, isAnyViewerOpen: Boolean = false, - isSelectionMode: Boolean = false, ) { val configuration = LocalConfiguration.current val screenWidth = configuration.screenWidthDp.dp @@ -130,17 +129,13 @@ fun ChannelMessageBubbleContainer( .pointerInput(Unit) { detectTapGestures( onTap = { offset -> - if (isSelectionMode) { - onSelectClick() - } else { - val clickPos = outerColumnPosition + offset - val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) - if (!bubbleRect.contains(clickPos)) { - onReplyClick(bubblePosition, bubbleSize, clickPos) - } + val clickPos = outerColumnPosition + offset + val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) + if (!bubbleRect.contains(clickPos)) { + onReplyClick(bubblePosition, bubbleSize, clickPos) } }, - onLongPress = { _ -> onSelectClick() } + onLongPress = { _ -> onLongReplyClick() } ) } ) { From 9dd56ffe3d27d4f93a60d5326f84b2928d06f0a9 Mon Sep 17 00:00:00 2001 From: Andro_Dev Date: Sat, 18 Apr 2026 14:23:24 +0300 Subject: [PATCH 08/12] Revert "fix compile" This reverts commit 959858d2fce2f078a79024ee15c1009a589f7a78. --- .../chatContent/ChatContentList.kt | 2 +- .../ChannelMessageBubbleContainer.kt | 5 +- .../components/MessageBubbleContainer.kt | 102 +++++++++++++++--- .../channels/ChannelMessageBubbleContainer.kt | 9 +- 4 files changed, 97 insertions(+), 21 deletions(-) diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt index 235e615e..1c9180e9 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt @@ -818,7 +818,7 @@ private fun MessageBubbleSwitcher( click ) }, - onLongReplyClick = { component.onToggleMessageSelection(item.messages.last().id) }, + onLongReplyClick = { component.onToggleMessageSelection(item.message.last().id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> if (isSelectionMode) component.onToggleMessageSelection(id) else component.onSendReaction( diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/ChannelMessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/ChannelMessageBubbleContainer.kt index bba82aed..133ff67c 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/ChannelMessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/ChannelMessageBubbleContainer.kt @@ -46,6 +46,7 @@ fun ChannelMessageBubbleContainer( onDocumentClick: (MessageModel) -> Unit = {}, onAudioClick: (MessageModel) -> Unit = {}, onReplyClick: (Offset, IntSize, Offset) -> Unit, + onLongReplyClick: () -> Unit = {}, onGoToReply: (MessageModel) -> Unit = {}, autoDownloadMobile: Boolean = false, autoDownloadWifi: Boolean = false, @@ -121,9 +122,7 @@ fun ChannelMessageBubbleContainer( onTap = { offset -> onReplyClick(bubblePosition, bubbleSize, bubblePosition + offset) }, - onLongPress = { offset -> - onReplyClick(bubblePosition, bubbleSize, bubblePosition + offset) - } + onLongPress = { _ -> onLongReplyClick() } ) } diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt index 6eecc6e8..876d2f65 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt @@ -259,7 +259,6 @@ fun MessageBubbleContainer( onAudioClick = onAudioClick, onCancelDownload = onCancelDownload, onReplyClick = onReplyClick, - onLongReplyClick = onLongReplyClick, onGoToReply = onGoToReply, onReactionClick = onReactionClick, onStickerClick = onStickerClick, @@ -351,7 +350,6 @@ private fun MessageContentSelector( onAudioClick: (MessageModel) -> Unit, onCancelDownload: (Int) -> Unit, onReplyClick: (Offset, IntSize, Offset) -> Unit, - onLongReplyClick: () -> Unit, onGoToReply: (MessageModel) -> Unit, onReactionClick: (Long, String) -> Unit, onStickerClick: (Long) -> Unit, @@ -391,7 +389,9 @@ private fun MessageContentSelector( onClick = { offset -> onReplyClick(bubblePosition, bubbleSize, bubblePosition + offset) }, - onLongClick = { onLongReplyClick() }, + onLongClick = { offset -> + onReplyClick(bubblePosition, bubbleSize, bubblePosition + offset) + }, toProfile = toProfile ) } @@ -405,7 +405,13 @@ private fun MessageContentSelector( onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, onStickerClick = { onStickerClick(it) }, - onLongClick = { onLongReplyClick() }, + onLongClick = { + onReplyClick( + bubblePosition, + bubbleSize, + bubblePosition + (bubbleSize.toSize() / 2f).toOffset() + ) + } ) } @@ -425,7 +431,13 @@ private fun MessageContentSelector( onPhotoClick = onPhotoClick, onDownloadPhoto = onDownloadPhoto, onCancelDownload = onCancelDownload, - onLongClick = { onLongReplyClick() }, + onLongClick = { offset -> + onReplyClick( + bubblePosition, + bubbleSize, + bubblePosition + offset + ) + }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -449,7 +461,13 @@ private fun MessageContentSelector( autoplayVideos = autoplayVideos, onVideoClick = onVideoClick, onCancelDownload = onCancelDownload, - onLongClick = { onLongReplyClick() }, + onLongClick = { offset -> + onReplyClick( + bubblePosition, + bubbleSize, + bubblePosition + offset + ) + }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -466,7 +484,13 @@ private fun MessageContentSelector( isOutgoing = isOutgoing, onVideoClick = onVideoClick, onCancelDownload = onCancelDownload, - onLongClick = { onLongReplyClick() }, + onLongClick = { offset -> + onReplyClick( + bubblePosition, + bubbleSize, + bubblePosition + offset + ) + }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) } ) @@ -488,7 +512,13 @@ private fun MessageContentSelector( autoDownloadRoaming = autoDownloadRoaming, onVoiceClick = onAudioClick, onCancelDownload = onCancelDownload, - onLongClick = { onLongReplyClick() }, + onLongClick = { offset -> + onReplyClick( + bubblePosition, + bubbleSize, + bubblePosition + offset + ) + }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -511,7 +541,13 @@ private fun MessageContentSelector( autoplayGifs = autoplayGifs, onGifClick = onVideoClick, onCancelDownload = onCancelDownload, - onLongClick = { onLongReplyClick() }, + onLongClick = { offset -> + onReplyClick( + bubblePosition, + bubbleSize, + bubblePosition + offset + ) + }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -536,7 +572,13 @@ private fun MessageContentSelector( autoDownloadRoaming = autoDownloadRoaming, onDocumentClick = onDocumentClick, onCancelDownload = onCancelDownload, - onLongClick = { onLongReplyClick() }, + onLongClick = { offset -> + onReplyClick( + bubblePosition, + bubbleSize, + bubblePosition + offset + ) + }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, downloadUtils = downloadUtils @@ -559,7 +601,13 @@ private fun MessageContentSelector( autoDownloadRoaming = autoDownloadRoaming, onAudioClick = onAudioClick, onCancelDownload = onCancelDownload, - onLongClick = { onLongReplyClick() }, + onLongClick = { offset -> + onReplyClick( + bubblePosition, + bubbleSize, + bubblePosition + offset + ) + }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, downloadUtils = downloadUtils @@ -578,7 +626,13 @@ private fun MessageContentSelector( bubbleRadius = bubbleRadius, isGroup = isGroup, onClick = { onGoToReply(msg) }, - onLongClick = { onLongReplyClick() }, + onLongClick = { + onReplyClick( + bubblePosition, + bubbleSize, + bubblePosition + (bubbleSize.toSize() / 2f).toOffset() + ) + }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile, @@ -598,7 +652,13 @@ private fun MessageContentSelector( bubbleRadius = bubbleRadius, onOptionClick = { onPollOptionClick(msg.id, it) }, onRetractVote = { onRetractVote(msg.id) }, - onLongClick = { onLongReplyClick() }, + onLongClick = { offset -> + onReplyClick( + bubblePosition, + bubbleSize, + bubblePosition + offset + ) + }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, onShowVoters = { onShowVoters(msg.id, it) }, @@ -619,7 +679,13 @@ private fun MessageContentSelector( isGroup = isGroup, bubbleRadius = bubbleRadius, onClick = { onGoToReply(msg) }, - onLongClick = { onLongReplyClick() }, + onLongClick = { + onReplyClick( + bubblePosition, + bubbleSize, + bubblePosition + (bubbleSize.toSize() / 2f).toOffset() + ) + }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile @@ -638,7 +704,13 @@ private fun MessageContentSelector( isGroup = isGroup, bubbleRadius = bubbleRadius, onClick = { onGoToReply(msg) }, - onLongClick = { onLongReplyClick() }, + onLongClick = { + onReplyClick( + bubblePosition, + bubbleSize, + bubblePosition + (bubbleSize.toSize() / 2f).toOffset() + ) + }, onReplyClick = onGoToReply, onReactionClick = { onReactionClick(msg.id, it) }, toProfile = toProfile diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt index 63f5ea51..79bae263 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt @@ -50,7 +50,6 @@ fun ChannelMessageBubbleContainer( onAudioClick: (MessageModel) -> Unit = {}, onCancelDownload: (Int) -> Unit = {}, onReplyClick: (Offset, IntSize, Offset) -> Unit, - onLongReplyClick: () -> Unit = {}, onGoToReply: (MessageModel) -> Unit = {}, autoDownloadMobile: Boolean = false, autoDownloadWifi: Boolean = false, @@ -135,7 +134,13 @@ fun ChannelMessageBubbleContainer( onReplyClick(bubblePosition, bubbleSize, clickPos) } }, - onLongPress = { _ -> onLongReplyClick() } + onLongPress = { offset -> + val clickPos = outerColumnPosition + offset + val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) + if (!bubbleRect.contains(clickPos)) { + onReplyClick(bubblePosition, bubbleSize, clickPos) + } + } ) } ) { From df789cdde2c10c199fda075c87d06fe0e4ac4e8e Mon Sep 17 00:00:00 2001 From: Andro_Dev Date: Sat, 18 Apr 2026 14:23:32 +0300 Subject: [PATCH 09/12] Revert "feat: selection mode on message long press" This reverts commit a7f315470f35d5c0cad578bbb4e1797203c25538. --- .../chats/currentChat/chatContent/ChatContentList.kt | 5 ----- .../components/AlbumMessageBubbleContainer.kt | 9 +++++++-- .../components/ChannelMessageBubbleContainer.kt | 5 +++-- .../currentChat/components/MessageBubbleContainer.kt | 9 +++++++-- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt index 1c9180e9..4f0ce731 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt @@ -605,7 +605,6 @@ private fun MessageBubbleSwitcher( click ) }, - onLongReplyClick = { component.onToggleMessageSelection(item.message.id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> if (isSelectionMode) component.onToggleMessageSelection(id) else component.onSendReaction( @@ -714,7 +713,6 @@ private fun MessageBubbleSwitcher( click ) }, - onLongReplyClick = { component.onToggleMessageSelection(item.message.id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> if (isSelectionMode) component.onToggleMessageSelection(id) else component.onSendReaction( @@ -818,7 +816,6 @@ private fun MessageBubbleSwitcher( click ) }, - onLongReplyClick = { component.onToggleMessageSelection(item.message.last().id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> if (isSelectionMode) component.onToggleMessageSelection(id) else component.onSendReaction( @@ -898,7 +895,6 @@ private fun RootMessageSection( onAudioClick = onAudioClick, onCancelDownload = { component.onCancelDownloadFile(it) }, onReplyClick = { pos, size, click -> onMessageOptionsClick(root, pos, size, click) }, - onLongReplyClick = { component.onToggleMessageSelection(root.id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> component.onSendReaction(id, r) }, onReplyMarkupButtonClick = { id, btn -> component.onReplyMarkupButtonClick(id, btn, root.senderId) }, @@ -936,7 +932,6 @@ private fun RootMessageSection( onAudioClick = onAudioClick, onCancelDownload = { component.onCancelDownloadFile(it) }, onReplyClick = { pos, size, click -> onMessageOptionsClick(root, pos, size, click) }, - onLongReplyClick = { component.onToggleMessageSelection(root.id) }, onGoToReply = onGoToReply, onReactionClick = { id, r -> component.onSendReaction(id, r) }, onReplyMarkupButtonClick = { id, btn -> component.onReplyMarkupButtonClick(id, btn, root.senderId) }, diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt index c9a5d418..28104ca8 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt @@ -63,7 +63,6 @@ fun AlbumMessageBubbleContainer( onAudioClick: (MessageModel) -> Unit = {}, onCancelDownload: (Int) -> Unit = {}, onReplyClick: (Offset, IntSize, Offset) -> Unit, - onLongReplyClick: () -> Unit = {}, onGoToReply: (MessageModel) -> Unit = {}, onReactionClick: (Long, String) -> Unit = { _, _ -> }, onReplyMarkupButtonClick: (Long, InlineKeyboardButtonModel) -> Unit = { _, _ -> }, @@ -173,7 +172,13 @@ fun AlbumMessageBubbleContainer( onReplyClick(bubblePosition, bubbleSize, clickPos) } }, - onLongPress = { _ -> onLongReplyClick() } + onLongPress = { offset -> + val clickPos = outerColumnPosition + offset + val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) + if (!bubbleRect.contains(clickPos)) { + onReplyClick(bubblePosition, bubbleSize, clickPos) + } + } ) } ) { diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/ChannelMessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/ChannelMessageBubbleContainer.kt index 133ff67c..bba82aed 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/ChannelMessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/ChannelMessageBubbleContainer.kt @@ -46,7 +46,6 @@ fun ChannelMessageBubbleContainer( onDocumentClick: (MessageModel) -> Unit = {}, onAudioClick: (MessageModel) -> Unit = {}, onReplyClick: (Offset, IntSize, Offset) -> Unit, - onLongReplyClick: () -> Unit = {}, onGoToReply: (MessageModel) -> Unit = {}, autoDownloadMobile: Boolean = false, autoDownloadWifi: Boolean = false, @@ -122,7 +121,9 @@ fun ChannelMessageBubbleContainer( onTap = { offset -> onReplyClick(bubblePosition, bubbleSize, bubblePosition + offset) }, - onLongPress = { _ -> onLongReplyClick() } + onLongPress = { offset -> + onReplyClick(bubblePosition, bubbleSize, bubblePosition + offset) + } ) } diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt index 876d2f65..bd774a8a 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt @@ -89,7 +89,6 @@ fun MessageBubbleContainer( onAudioClick: (MessageModel) -> Unit = {}, onCancelDownload: (Int) -> Unit = {}, onReplyClick: (Offset, IntSize, Offset) -> Unit, - onLongReplyClick: () -> Unit = {}, onGoToReply: (MessageModel) -> Unit = {}, onReactionClick: (Long, String) -> Unit = { _, _ -> }, onStickerClick: (Long) -> Unit = {}, @@ -201,7 +200,13 @@ fun MessageBubbleContainer( onReplyClick(bubblePosition, bubbleSize, clickPos) } }, - onLongPress = { _ -> onLongReplyClick() } + onLongPress = { offset -> + val clickPos = outerColumnPosition + offset + val bubbleRect = Rect(bubblePosition, bubbleSize.toSize()) + if (!bubbleRect.contains(clickPos)) { + onReplyClick(bubblePosition, bubbleSize, clickPos) + } + } ) } ) { From c85406a20647148c16bde4c52311e632d8a93bfc Mon Sep 17 00:00:00 2001 From: Andro_Dev Date: Sat, 18 Apr 2026 14:38:11 +0300 Subject: [PATCH 10/12] adaptive fast reply for chat permissions --- .../chatContent/ChatContentList.kt | 60 ++++++++++++++++++- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt index 4f0ce731..eeb26113 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/chatContent/ChatContentList.kt @@ -559,6 +559,60 @@ private fun MessageBubbleSwitcher( val isChannel = state.isChannel && state.currentTopicId == null val isTopicClosed = state.topics.find { it.id.toLong() == state.currentTopicId }?.isClosed?: false + val canWriteText by remember(state.isAdmin, state.permissions.canSendBasicMessages) { + derivedStateOf { state.isAdmin || state.permissions.canSendBasicMessages } + } + val canSendPhotos by remember(state.isAdmin, state.permissions.canSendPhotos) { + derivedStateOf { state.isAdmin || state.permissions.canSendPhotos } + } + val canSendVideos by remember(state.isAdmin, state.permissions.canSendVideos) { + derivedStateOf { state.isAdmin || state.permissions.canSendVideos } + } + val canSendDocuments by remember(state.isAdmin, state.permissions.canSendDocuments) { + derivedStateOf { state.isAdmin || state.permissions.canSendDocuments } + } + val canSendAudios by remember(state.isAdmin, state.permissions.canSendAudios) { + derivedStateOf { state.isAdmin || state.permissions.canSendAudios } + } + val canUseMediaPicker by remember(canSendPhotos, canSendVideos) { + derivedStateOf { canSendPhotos || canSendVideos } + } + val canUseDocumentPicker by remember(canSendDocuments, canSendAudios) { + derivedStateOf { canSendDocuments || canSendAudios } + } + val canSendPolls by remember(state.isAdmin, state.permissions.canSendPolls) { + derivedStateOf { state.isAdmin || state.permissions.canSendPolls } + } + val canOpenAttachSheet by remember( + canUseMediaPicker, + canUseDocumentPicker, + canSendPolls, + state.attachMenuBots + ) { + derivedStateOf { canUseMediaPicker || canUseDocumentPicker || canSendPolls || state.attachMenuBots.isNotEmpty() } + } + val canSendStickers by remember(state.isAdmin, state.permissions.canSendOtherMessages) { + derivedStateOf { state.isAdmin || state.permissions.canSendOtherMessages } + } + val canSendVoice by remember(state.isAdmin, state.permissions.canSendVoiceNotes) { + derivedStateOf { state.isAdmin || state.permissions.canSendVoiceNotes } + } + val canSendVideoNotes by remember(state.isAdmin, state.permissions.canSendVideoNotes) { + derivedStateOf { state.isAdmin || state.permissions.canSendVideoNotes } + } + val canSendAnything by remember( + canWriteText, + canOpenAttachSheet, + canSendStickers, + canSendVoice, + canSendVideoNotes, + canSendPolls + ) { + derivedStateOf { + canWriteText || canOpenAttachSheet || canSendStickers || canSendVoice || canSendVideoNotes || canSendPolls + } + } + when (item) { is GroupedMessageItem.Single -> { if (item.message.content is MessageContent.Service) { @@ -655,7 +709,7 @@ private fun MessageBubbleSwitcher( onCommentsClick = { component.onCommentsClick(it) }, toProfile = toProfile, onViaBotClick = onViaBotClick, - canReply = state.canWrite && !isSelectionMode, + canReply = state.canWrite && !isSelectionMode && canSendAnything, onReplySwipe = { component.onReplyMessage(it) }, onYouTubeClick = { component.onOpenYouTube(it) }, onInstantViewClick = { component.onOpenInstantView(it) }, @@ -760,7 +814,7 @@ private fun MessageBubbleSwitcher( onPositionChange = { _, pos, size -> onMessagePositionChange(pos, size) }, toProfile = toProfile, onViaBotClick = onViaBotClick, - canReply = state.canWrite && !isSelectionMode && (!isTopicClosed || state.isAdmin), + canReply = state.canWrite && !isSelectionMode && (!isTopicClosed || state.isAdmin) && canSendAnything, onReplySwipe = { component.onReplyMessage(it) }, swipeEnabled = !isSelectionMode, downloadUtils = downloadUtils, @@ -828,7 +882,7 @@ private fun MessageBubbleSwitcher( onCommentsClick = { component.onCommentsClick(it) }, toProfile = toProfile, onViaBotClick = onViaBotClick, - canReply = state.canWrite && !isSelectionMode && (!isTopicClosed || state.isAdmin), + canReply = state.canWrite && !isSelectionMode && (!isTopicClosed || state.isAdmin) && canSendAnything, onReplySwipe = { component.onReplyMessage(it) }, swipeEnabled = !isSelectionMode, downloadUtils = downloadUtils, From 5ed50f56637bea01ebfc7cc5c734afe4dbf06fbc Mon Sep 17 00:00:00 2001 From: Andro_Dev Date: Sat, 18 Apr 2026 18:52:56 +0300 Subject: [PATCH 11/12] improve fast reply indicator ui --- .../components/FastReplyIndicator.kt | 89 ++++++++++--------- 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/FastReplyIndicator.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/FastReplyIndicator.kt index 9cf04035..f9a240b4 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/FastReplyIndicator.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/FastReplyIndicator.kt @@ -1,24 +1,26 @@ package org.monogram.presentation.features.chats.currentChat.components +import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.AnimationVector1D import androidx.compose.animation.core.Spring -import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.spring -import androidx.compose.animation.core.tween -import androidx.compose.foundation.background +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.scaleIn +import androidx.compose.animation.scaleOut import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.Reply +import androidx.compose.material3.CircularWavyProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer @@ -36,6 +38,7 @@ const val REPLY_TRIGGER_FRACTION = 0.35f const val MAX_SWIPE_FRACTION = 0.7f const val ICON_OFFSET_FRACTION = 0.1f +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun FastReplyIndicator( modifier: Modifier = Modifier, @@ -46,46 +49,44 @@ fun FastReplyIndicator( ) { val triggerDistance = maxWidth.value * REPLY_TRIGGER_FRACTION val dragged = (-dragOffsetX.value).coerceAtLeast(0f) - val progress = ((dragged - 48.dp.value) / (triggerDistance - 48.dp.value)) - .coerceIn(0f, 1f) - val iconAlpha by animateFloatAsState( - targetValue = progress, - animationSpec = tween(durationMillis = 150) - ) - val iconScale by animateFloatAsState( - targetValue = lerp(0.5f, 1f, progress), - animationSpec = spring(dampingRatio = Spring.DampingRatioMediumBouncy, stiffness = Spring.StiffnessLow) - ) + val progress = ((dragged - 48.dp.value) / (triggerDistance - 48.dp.value)).coerceIn(0f, 1f) + val iconOffset = maxWidth * ICON_OFFSET_FRACTION - if (dragged > 48.dp.value) { - Box( - modifier = modifier - .offset(x = if (isOutgoing) iconOffset else maxWidth) - .size(30.dp) - .graphicsLayer { - translationX = when { - isOutgoing -> (-dragOffsetX.value - iconOffset.value) * 0.5f - inverseOffset -> -iconOffset.value - else -> iconOffset.value - } - scaleX = iconScale - scaleY = iconScale - alpha = iconAlpha + Box( + modifier = modifier + .offset(x = if (isOutgoing) iconOffset else maxWidth) + .size(34.dp) + .graphicsLayer { + translationX = when { + isOutgoing -> (-dragOffsetX.value - iconOffset.value) * 0.5f + inverseOffset -> -iconOffset.value + else -> iconOffset.value } - .background( - color = MaterialTheme.colorScheme.surfaceContainerHigh.copy(alpha = 0.7f), - shape = CircleShape - ), - contentAlignment = Alignment.Center + }, + contentAlignment = Alignment.Center + ) { + AnimatedVisibility( + visible = dragged > 48.dp.value, + enter = fadeIn() + scaleIn(initialScale = 0.6f), + exit = fadeOut() + scaleOut(targetScale = 0.6f) ) { - Icon( - imageVector = Icons.AutoMirrored.Filled.Reply, - contentDescription = null, - tint = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.size(18.dp) - ) + Box( + modifier = Modifier.matchParentSize(), + contentAlignment = Alignment.Center + ) { + CircularWavyProgressIndicator( + progress = { progress }, + color = MaterialTheme.colorScheme.primary, + ) + Icon( + imageVector = Icons.AutoMirrored.Filled.Reply, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + modifier = Modifier.size(18.dp) + ) + } } } } @@ -135,7 +136,13 @@ fun Modifier.fastReplyPointer( onReplySwipe() } scope.launch { - dragOffsetX.animateTo(0f, spring()) + dragOffsetX.animateTo( + 0f, + spring( + dampingRatio = Spring.DampingRatioMediumBouncy, + stiffness = Spring.StiffnessLow + ) + ) } } } From b972d858f38720206786f008c39750b294b69932 Mon Sep 17 00:00:00 2001 From: Andro_Dev Date: Sat, 18 Apr 2026 21:29:17 +0300 Subject: [PATCH 12/12] improve fast reply indicator ui --- .../currentChat/components/AlbumMessageBubbleContainer.kt | 3 +-- .../chats/currentChat/components/FastReplyIndicator.kt | 3 +-- .../chats/currentChat/components/MessageBubbleContainer.kt | 3 +-- .../components/channels/ChannelMessageBubbleContainer.kt | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt index 28104ca8..89888607 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/AlbumMessageBubbleContainer.kt @@ -312,8 +312,7 @@ fun AlbumMessageBubbleContainer( } FastReplyIndicator( - modifier = Modifier - .align(if (isOutgoing) Alignment.CenterEnd else Alignment.CenterStart), + modifier = Modifier.align(Alignment.CenterEnd), dragOffsetX = dragOffsetX, isOutgoing = isOutgoing, maxWidth = maxWidth, diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/FastReplyIndicator.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/FastReplyIndicator.kt index f9a240b4..bcc5a3ef 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/FastReplyIndicator.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/FastReplyIndicator.kt @@ -30,7 +30,6 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.positionChange import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import androidx.compose.ui.util.lerp import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -56,7 +55,7 @@ fun FastReplyIndicator( Box( modifier = modifier - .offset(x = if (isOutgoing) iconOffset else maxWidth) + .offset(x = iconOffset) .size(34.dp) .graphicsLayer { translationX = when { diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt index bd774a8a..67f7737b 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/MessageBubbleContainer.kt @@ -294,8 +294,7 @@ fun MessageBubbleContainer( } FastReplyIndicator( - modifier = Modifier - .align(if (isOutgoing) Alignment.CenterEnd else Alignment.CenterStart), + modifier = Modifier.align(Alignment.CenterEnd), dragOffsetX = dragOffsetX, isOutgoing = isOutgoing, maxWidth = maxWidth diff --git a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt index 79bae263..dfeeb4ed 100644 --- a/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt +++ b/presentation/src/main/java/org/monogram/presentation/features/chats/currentChat/components/channels/ChannelMessageBubbleContainer.kt @@ -418,7 +418,7 @@ fun ChannelMessageBubbleContainer( } FastReplyIndicator( - modifier = Modifier.align(Alignment.CenterStart), + modifier = Modifier.align(Alignment.CenterEnd), dragOffsetX = dragOffsetX, inverseOffset = isLandscape, maxWidth = maxWidth,