Allow replying to a message with an attachment (#5261)
This commit is contained in:
parent
670c929993
commit
a8a6a51953
10 changed files with 57 additions and 34 deletions
|
|
@ -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<Attachment>) {
|
||||
override fun onPreviewAttachments(attachments: ImmutableList<Attachment>, 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<AttachmentsPreviewNode>(buildContext, listOf(inputs))
|
||||
}
|
||||
|
|
@ -416,10 +418,11 @@ class MessagesFlowNode(
|
|||
)
|
||||
}
|
||||
|
||||
override fun onPreviewAttachments(attachments: ImmutableList<Attachment>) {
|
||||
override fun onPreviewAttachments(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?) {
|
||||
backstack.push(NavTarget.AttachmentPreview(
|
||||
attachment = attachments.first(),
|
||||
timelineMode = Timeline.Mode.Thread(navTarget.threadRootId)
|
||||
timelineMode = Timeline.Mode.Thread(navTarget.threadRootId),
|
||||
inReplyToEventId = inReplyToEventId,
|
||||
))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ interface MessagesNavigator {
|
|||
fun onForwardEventClick(eventId: EventId)
|
||||
fun onReportContentClick(eventId: EventId, senderId: UserId)
|
||||
fun onEditPollClick(eventId: EventId)
|
||||
fun onPreviewAttachment(attachments: ImmutableList<Attachment>)
|
||||
fun onPreviewAttachment(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?)
|
||||
fun onNavigateToRoom(roomId: RoomId, serverNames: List<String>)
|
||||
fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ class MessagesNode(
|
|||
interface Callback : Plugin {
|
||||
fun onRoomDetailsClick()
|
||||
fun onEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean
|
||||
fun onPreviewAttachments(attachments: ImmutableList<Attachment>)
|
||||
fun onPreviewAttachments(attachments: ImmutableList<Attachment>, 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<Attachment>) {
|
||||
callbacks.forEach { it.onPreviewAttachments(attachments) }
|
||||
override fun onPreviewAttachment(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?) {
|
||||
callbacks.forEach { it.onPreviewAttachments(attachments, inReplyToEventId) }
|
||||
}
|
||||
|
||||
override fun onNavigateToRoom(roomId: RoomId, serverNames: List<String>) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ->
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ class ThreadedMessagesNode(
|
|||
|
||||
interface Callback : Plugin {
|
||||
fun onEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean
|
||||
fun onPreviewAttachments(attachments: ImmutableList<Attachment>)
|
||||
fun onPreviewAttachments(attachments: ImmutableList<Attachment>, 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<Attachment>) {
|
||||
callbacks.forEach { it.onPreviewAttachments(attachments) }
|
||||
override fun onPreviewAttachment(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?) {
|
||||
callbacks.forEach { it.onPreviewAttachments(attachments, inReplyToEventId) }
|
||||
}
|
||||
|
||||
override fun onNavigateToRoom(roomId: RoomId, serverNames: List<String>) = Unit
|
||||
|
|
|
|||
|
|
@ -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<Attachment>) -> Unit = { _ -> lambdaError() },
|
||||
private val onPreviewAttachmentLambda: (attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?) -> Unit = { _, _ -> lambdaError() },
|
||||
private val onNavigateToRoomLambda: (roomId: RoomId, serverNames: List<String>) -> 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<Attachment>) {
|
||||
onPreviewAttachmentLambda(attachments)
|
||||
override fun onPreviewAttachment(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?) {
|
||||
onPreviewAttachmentLambda(attachments, inReplyToEventId)
|
||||
}
|
||||
|
||||
override fun onNavigateToRoom(roomId: RoomId, serverNames: List<String>) {
|
||||
|
|
|
|||
|
|
@ -618,6 +618,7 @@ class AttachmentsPreviewPresenterTest {
|
|||
dispatchers = testCoroutineDispatchers(),
|
||||
mediaOptimizationSelectorPresenterFactory = mediaOptimizationSelectorPresenterFactory,
|
||||
timelineMode = timelineMode,
|
||||
inReplyToEventId = null,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -688,7 +688,7 @@ class MessageComposerPresenterTest {
|
|||
val room = FakeJoinedRoom(
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList<Attachment> -> }
|
||||
val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList<Attachment>, _: EventId? -> }
|
||||
val navigator = FakeMessagesNavigator(
|
||||
onPreviewAttachmentLambda = onPreviewAttachmentLambda
|
||||
)
|
||||
|
|
@ -728,7 +728,7 @@ class MessageComposerPresenterTest {
|
|||
val room = FakeJoinedRoom(
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList<Attachment> -> }
|
||||
val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList<Attachment>, _: EventId? -> }
|
||||
val navigator = FakeMessagesNavigator(
|
||||
onPreviewAttachmentLambda = onPreviewAttachmentLambda
|
||||
)
|
||||
|
|
@ -785,7 +785,7 @@ class MessageComposerPresenterTest {
|
|||
val room = FakeJoinedRoom(
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList<Attachment> -> }
|
||||
val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList<Attachment>, _: 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<Attachment> -> }
|
||||
val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList<Attachment>, _: EventId? -> }
|
||||
val navigator = FakeMessagesNavigator(
|
||||
onPreviewAttachmentLambda = onPreviewAttachmentLambda
|
||||
)
|
||||
|
|
@ -870,7 +870,7 @@ class MessageComposerPresenterTest {
|
|||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val permissionPresenter = FakePermissionsPresenter()
|
||||
val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList<Attachment> -> }
|
||||
val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList<Attachment>, _: 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<Attachment> -> }
|
||||
val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList<Attachment>, _: EventId? -> }
|
||||
val navigator = FakeMessagesNavigator(
|
||||
onPreviewAttachmentLambda = onPreviewAttachmentLambda
|
||||
)
|
||||
|
|
@ -920,7 +920,7 @@ class MessageComposerPresenterTest {
|
|||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val permissionPresenter = FakePermissionsPresenter()
|
||||
val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList<Attachment> -> }
|
||||
val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList<Attachment>, _: EventId? -> }
|
||||
val navigator = FakeMessagesNavigator(
|
||||
onPreviewAttachmentLambda = onPreviewAttachmentLambda
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue