diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index 29b62a8d43..cb13e50d94 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -142,7 +142,7 @@ class MessagesFlowNode( ) : NavTarget @Parcelize - data class AttachmentPreview(val timelineMode: Timeline.Mode, val attachment: Attachment) : NavTarget + data class AttachmentPreview(val timelineMode: Timeline.Mode, val attachment: Attachment, val inReplyToEventId: EventId?) : NavTarget @Parcelize data class LocationViewer(val location: Location, val description: String?) : NavTarget @@ -224,10 +224,11 @@ class MessagesFlowNode( ) } - override fun onPreviewAttachments(attachments: ImmutableList) { + override fun onPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) { backstack.push(NavTarget.AttachmentPreview( attachment = attachments.first(), timelineMode = Timeline.Mode.Live, + inReplyToEventId = inReplyToEventId, )) } @@ -314,6 +315,7 @@ class MessagesFlowNode( val inputs = AttachmentsPreviewNode.Inputs( attachment = navTarget.attachment, timelineMode = navTarget.timelineMode, + inReplyToEventId = navTarget.inReplyToEventId, ) createNode(buildContext, listOf(inputs)) } @@ -416,10 +418,11 @@ class MessagesFlowNode( ) } - override fun onPreviewAttachments(attachments: ImmutableList) { + override fun onPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) { backstack.push(NavTarget.AttachmentPreview( attachment = attachments.first(), - timelineMode = Timeline.Mode.Thread(navTarget.threadRootId) + timelineMode = Timeline.Mode.Thread(navTarget.threadRootId), + inReplyToEventId = inReplyToEventId, )) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt index ad8e6c6081..250e271880 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt @@ -20,7 +20,7 @@ interface MessagesNavigator { fun onForwardEventClick(eventId: EventId) fun onReportContentClick(eventId: EventId, senderId: UserId) fun onEditPollClick(eventId: EventId) - fun onPreviewAttachment(attachments: ImmutableList) + fun onPreviewAttachment(attachments: ImmutableList, inReplyToEventId: EventId?) fun onNavigateToRoom(roomId: RoomId, serverNames: List) fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index 27fcdcd5d7..244b9a1805 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -112,7 +112,7 @@ class MessagesNode( interface Callback : Plugin { fun onRoomDetailsClick() fun onEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean - fun onPreviewAttachments(attachments: ImmutableList) + fun onPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) fun onUserDataClick(userId: UserId) fun onPermalinkClick(data: PermalinkData) fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) @@ -219,8 +219,8 @@ class MessagesNode( callbacks.forEach { it.onEditPollClick(eventId) } } - override fun onPreviewAttachment(attachments: ImmutableList) { - callbacks.forEach { it.onPreviewAttachments(attachments) } + override fun onPreviewAttachment(attachments: ImmutableList, inReplyToEventId: EventId?) { + callbacks.forEach { it.onPreviewAttachments(attachments, inReplyToEventId) } } override fun onNavigateToRoom(roomId: RoomId, serverNames: List) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt index d94e69ac22..8648354e7a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt @@ -20,6 +20,7 @@ import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.mediaviewer.api.local.LocalMediaRenderer @@ -34,6 +35,7 @@ class AttachmentsPreviewNode( data class Inputs( val attachment: Attachment, val timelineMode: Timeline.Mode, + val inReplyToEventId: EventId?, ) : NodeInputs private val inputs: Inputs = inputs() @@ -46,6 +48,7 @@ class AttachmentsPreviewNode( attachment = inputs.attachment, timelineMode = inputs.timelineMode, onDoneListener = onDoneListener, + inReplyToEventId = inputs.inReplyToEventId, ) @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt index a0452c8acf..d33dbaf440 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt @@ -54,6 +54,7 @@ class AttachmentsPreviewPresenter( @Assisted private val attachment: Attachment, @Assisted private val onDoneListener: OnDoneListener, @Assisted private val timelineMode: Timeline.Mode, + @Assisted private val inReplyToEventId: EventId?, mediaSenderFactory: MediaSender.Factory, private val permalinkBuilder: PermalinkBuilder, private val temporaryUriDeleter: TemporaryUriDeleter, @@ -67,6 +68,7 @@ class AttachmentsPreviewPresenter( attachment: Attachment, timelineMode: Timeline.Mode, onDoneListener: OnDoneListener, + inReplyToEventId: EventId?, ): AttachmentsPreviewPresenter } @@ -182,7 +184,7 @@ class AttachmentsPreviewPresenter( caption = caption, sendActionState = sendActionState, dismissAfterSend = false, - inReplyToEventId = null, + inReplyToEventId = inReplyToEventId, ) // Clean up the pre-processed media after it's been sent diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index 68153e01fc..70a79938b3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -45,6 +45,7 @@ import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.di.annotations.SessionCoroutineScope +import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder import io.element.android.libraries.matrix.api.permalink.PermalinkParser @@ -54,12 +55,10 @@ import io.element.android.libraries.matrix.api.room.draft.ComposerDraft import io.element.android.libraries.matrix.api.room.draft.ComposerDraftType import io.element.android.libraries.matrix.api.room.getDirectRoomMember import io.element.android.libraries.matrix.api.room.isDm -import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.matrix.api.timeline.TimelineException import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails import io.element.android.libraries.matrix.ui.messages.reply.map -import io.element.android.libraries.matrix.ui.room.getDirectRoomMember import io.element.android.libraries.mediapickers.api.PickerProvider import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProvider import io.element.android.libraries.mediaupload.api.MediaSender @@ -247,16 +246,23 @@ class MessageComposerPresenter( richTextEditorState = richTextEditorState, ) } - is MessageComposerEvents.SendUri -> sessionCoroutineScope.sendAttachment( - attachment = Attachment.Media( - localMedia = localMediaFactory.createFromUri( - uri = event.uri, - mimeType = null, - name = null, - formattedFileSize = null + is MessageComposerEvents.SendUri -> { + val inReplyToEventId = (messageComposerContext.composerMode as? MessageComposerMode.Reply)?.eventId + sessionCoroutineScope.sendAttachment( + attachment = Attachment.Media( + localMedia = localMediaFactory.createFromUri( + uri = event.uri, + mimeType = null, + name = null, + formattedFileSize = null + ), ), - ), - ) + inReplyToEventId = inReplyToEventId, + ) + + // Reset composer since the attachment has been sent + messageComposerContext.composerMode = MessageComposerMode.Normal + } is MessageComposerEvents.SetMode -> { localCoroutineScope.setMode(event.composerMode, markdownTextEditorState, richTextEditorState) } @@ -497,12 +503,14 @@ class MessageComposerPresenter( private fun CoroutineScope.sendAttachment( attachment: Attachment, + inReplyToEventId: EventId?, ) = when (attachment) { is Attachment.Media -> { launch { sendMedia( uri = attachment.localMedia.uri, mimeType = attachment.localMedia.info.mimeType, + inReplyToEventId = inReplyToEventId, ) } } @@ -521,17 +529,23 @@ class MessageComposerPresenter( formattedFileSize = null ) val mediaAttachment = Attachment.Media(localMedia) - navigator.onPreviewAttachment(persistentListOf(mediaAttachment)) + val inReplyToEventId = (messageComposerContext.composerMode as? MessageComposerMode.Reply)?.eventId + navigator.onPreviewAttachment(persistentListOf(mediaAttachment), inReplyToEventId) + + // Reset composer since the attachment will be sent in a separate flow + messageComposerContext.composerMode = MessageComposerMode.Normal } private suspend fun sendMedia( uri: Uri, mimeType: String, + inReplyToEventId: EventId?, ) = runCatchingExceptions { mediaSender.sendMedia( uri = uri, mimeType = mimeType, mediaOptimizationConfig = mediaOptimizationConfigProvider.get(), + inReplyToEventId = inReplyToEventId, ).getOrThrow() } .onFailure { cause -> diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt index 4c8d42fb3f..a7ff70e772 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt @@ -115,7 +115,7 @@ class ThreadedMessagesNode( interface Callback : Plugin { fun onEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean - fun onPreviewAttachments(attachments: ImmutableList) + fun onPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) fun onUserDataClick(userId: UserId) fun onPermalinkClick(data: PermalinkData) fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) @@ -215,8 +215,8 @@ class ThreadedMessagesNode( callbacks.forEach { it.onEditPollClick(eventId) } } - override fun onPreviewAttachment(attachments: ImmutableList) { - callbacks.forEach { it.onPreviewAttachments(attachments) } + override fun onPreviewAttachment(attachments: ImmutableList, inReplyToEventId: EventId?) { + callbacks.forEach { it.onPreviewAttachments(attachments, inReplyToEventId) } } override fun onNavigateToRoom(roomId: RoomId, serverNames: List) = Unit diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt index c90ee16e47..7320671694 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt @@ -21,7 +21,7 @@ class FakeMessagesNavigator( private val onForwardEventClickLambda: (eventId: EventId) -> Unit = { _ -> lambdaError() }, private val onReportContentClickLambda: (eventId: EventId, senderId: UserId) -> Unit = { _, _ -> lambdaError() }, private val onEditPollClickLambda: (eventId: EventId) -> Unit = { _ -> lambdaError() }, - private val onPreviewAttachmentLambda: (attachments: ImmutableList) -> Unit = { _ -> lambdaError() }, + private val onPreviewAttachmentLambda: (attachments: ImmutableList, inReplyToEventId: EventId?) -> Unit = { _, _ -> lambdaError() }, private val onNavigateToRoomLambda: (roomId: RoomId, serverNames: List) -> Unit = { _, _ -> lambdaError() }, private val onOpenThreadLambda: (threadRootId: ThreadId, focusedEventId: EventId?) -> Unit = { _, _ -> lambdaError() }, ) : MessagesNavigator { @@ -41,8 +41,8 @@ class FakeMessagesNavigator( onEditPollClickLambda(eventId) } - override fun onPreviewAttachment(attachments: ImmutableList) { - onPreviewAttachmentLambda(attachments) + override fun onPreviewAttachment(attachments: ImmutableList, inReplyToEventId: EventId?) { + onPreviewAttachmentLambda(attachments, inReplyToEventId) } override fun onNavigateToRoom(roomId: RoomId, serverNames: List) { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt index 0f1da066bd..941a81d075 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt @@ -618,6 +618,7 @@ class AttachmentsPreviewPresenterTest { dispatchers = testCoroutineDispatchers(), mediaOptimizationSelectorPresenterFactory = mediaOptimizationSelectorPresenterFactory, timelineMode = timelineMode, + inReplyToEventId = null, ) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt index 84aeb2ce28..735fdaaabe 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt @@ -688,7 +688,7 @@ class MessageComposerPresenterTest { val room = FakeJoinedRoom( typingNoticeResult = { Result.success(Unit) } ) - val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList -> } + val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList, _: EventId? -> } val navigator = FakeMessagesNavigator( onPreviewAttachmentLambda = onPreviewAttachmentLambda ) @@ -728,7 +728,7 @@ class MessageComposerPresenterTest { val room = FakeJoinedRoom( typingNoticeResult = { Result.success(Unit) } ) - val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList -> } + val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList, _: EventId? -> } val navigator = FakeMessagesNavigator( onPreviewAttachmentLambda = onPreviewAttachmentLambda ) @@ -785,7 +785,7 @@ class MessageComposerPresenterTest { val room = FakeJoinedRoom( typingNoticeResult = { Result.success(Unit) } ) - val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList -> } + val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList, _: EventId? -> } val navigator = FakeMessagesNavigator( onPreviewAttachmentLambda = onPreviewAttachmentLambda ) @@ -846,7 +846,7 @@ class MessageComposerPresenterTest { typingNoticeResult = { Result.success(Unit) } ) val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() } - val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList -> } + val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList, _: EventId? -> } val navigator = FakeMessagesNavigator( onPreviewAttachmentLambda = onPreviewAttachmentLambda ) @@ -870,7 +870,7 @@ class MessageComposerPresenterTest { typingNoticeResult = { Result.success(Unit) } ) val permissionPresenter = FakePermissionsPresenter() - val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList -> } + val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList, _: EventId? -> } val navigator = FakeMessagesNavigator( onPreviewAttachmentLambda = onPreviewAttachmentLambda ) @@ -896,7 +896,7 @@ class MessageComposerPresenterTest { typingNoticeResult = { Result.success(Unit) } ) val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() } - val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList -> } + val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList, _: EventId? -> } val navigator = FakeMessagesNavigator( onPreviewAttachmentLambda = onPreviewAttachmentLambda ) @@ -920,7 +920,7 @@ class MessageComposerPresenterTest { typingNoticeResult = { Result.success(Unit) } ) val permissionPresenter = FakePermissionsPresenter() - val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList -> } + val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList, _: EventId? -> } val navigator = FakeMessagesNavigator( onPreviewAttachmentLambda = onPreviewAttachmentLambda )