Skip to content

Commit bb370e0

Browse files
committed
Support gallery messages
1 parent 9699488 commit bb370e0

File tree

58 files changed

+2252
-215
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2252
-215
lines changed

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
4646
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
4747
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentWithAttachment
4848
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
49+
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemGalleryContent
4950
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
5051
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
5152
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
@@ -143,10 +144,11 @@ class MessagesFlowNode(
143144
val mediaInfo: MediaInfo,
144145
val mediaSource: MediaSource,
145146
val thumbnailSource: MediaSource?,
147+
val galleryItems: List<io.element.android.libraries.mediaviewer.api.GalleryItemData> = emptyList(),
146148
) : NavTarget
147149

148150
@Parcelize
149-
data class AttachmentPreview(val timelineMode: Timeline.Mode, val attachment: Attachment, val inReplyToEventId: EventId?) : NavTarget
151+
data class AttachmentPreview(val timelineMode: Timeline.Mode, val attachments: ImmutableList<Attachment>, val inReplyToEventId: EventId?) : NavTarget
150152

151153
@Parcelize
152154
data class LocationViewer(val mode: ShowLocationMode) : NavTarget
@@ -227,17 +229,18 @@ class MessagesFlowNode(
227229
callback.navigateToRoomDetails()
228230
}
229231

230-
override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean {
232+
override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event, galleryItemIndex: Int?): Boolean {
231233
return processEventClick(
232234
timelineMode = timelineMode,
233235
event = event,
236+
galleryItemIndex = galleryItemIndex,
234237
)
235238
}
236239

237240
override fun navigateToPreviewAttachments(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?) {
238241
backstack.push(
239242
NavTarget.AttachmentPreview(
240-
attachment = attachments.first(),
243+
attachments = attachments,
241244
timelineMode = Timeline.Mode.Live,
242245
inReplyToEventId = inReplyToEventId,
243246
)
@@ -317,6 +320,7 @@ class MessagesFlowNode(
317320
mediaSource = navTarget.mediaSource,
318321
thumbnailSource = navTarget.thumbnailSource,
319322
canShowInfo = true,
323+
galleryItems = navTarget.galleryItems,
320324
)
321325
val callback = object : MediaViewerEntryPoint.Callback {
322326
override fun onDone() {
@@ -341,7 +345,7 @@ class MessagesFlowNode(
341345
}
342346
is NavTarget.AttachmentPreview -> {
343347
val inputs = AttachmentsPreviewNode.Inputs(
344-
attachment = navTarget.attachment,
348+
attachments = navTarget.attachments,
345349
timelineMode = navTarget.timelineMode,
346350
inReplyToEventId = navTarget.inReplyToEventId,
347351
)
@@ -456,17 +460,18 @@ class MessagesFlowNode(
456460
focusedEventId = navTarget.focusedEventId,
457461
)
458462
val callback = object : ThreadedMessagesNode.Callback {
459-
override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean {
463+
override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event, galleryItemIndex: Int?): Boolean {
460464
return processEventClick(
461465
timelineMode = timelineMode,
462466
event = event,
467+
galleryItemIndex = galleryItemIndex,
463468
)
464469
}
465470

466471
override fun navigateToPreviewAttachments(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?) {
467472
backstack.push(
468473
NavTarget.AttachmentPreview(
469-
attachment = attachments.first(),
474+
attachments = attachments,
470475
timelineMode = Timeline.Mode.Thread(navTarget.threadRootId),
471476
inReplyToEventId = inReplyToEventId,
472477
)
@@ -547,6 +552,7 @@ class MessagesFlowNode(
547552
private fun processEventClick(
548553
timelineMode: Timeline.Mode,
549554
event: TimelineItem.Event,
555+
galleryItemIndex: Int? = null,
550556
): Boolean {
551557
val navTarget = when (event.content) {
552558
is TimelineItemImageContent -> {
@@ -598,6 +604,58 @@ class MessagesFlowNode(
598604
mode = mode
599605
).takeIf { locationService.isServiceAvailable() }
600606
}
607+
is TimelineItemGalleryContent -> {
608+
val item = if (galleryItemIndex != null) {
609+
event.content.items.getOrNull(galleryItemIndex)
610+
} else {
611+
event.content.items.firstOrNull()
612+
} ?: return false
613+
val mediaInfo = MediaInfo(
614+
filename = item.filename,
615+
fileSize = null,
616+
caption = event.content.caption,
617+
mimeType = item.mimeType,
618+
formattedFileSize = "",
619+
fileExtension = item.filename.substringAfterLast('.', ""),
620+
senderId = event.senderId,
621+
senderName = event.safeSenderName,
622+
senderAvatar = event.senderAvatar.url,
623+
dateSent = dateFormatter.format(
624+
event.sentTimeMillis,
625+
mode = DateFormatterMode.Day,
626+
),
627+
dateSentFull = dateFormatter.format(
628+
timestamp = event.sentTimeMillis,
629+
mode = DateFormatterMode.Full,
630+
),
631+
waveform = null,
632+
duration = null,
633+
)
634+
val galleryItems = event.content.items.map { galleryItem ->
635+
io.element.android.libraries.mediaviewer.api.GalleryItemData(
636+
filename = galleryItem.filename,
637+
mimeType = galleryItem.mimeType,
638+
mediaSource = galleryItem.mediaSource,
639+
thumbnailSource = galleryItem.thumbnailSource,
640+
isVideo = galleryItem.isVideo,
641+
isAudio = galleryItem.isAudio,
642+
isFile = galleryItem.isFile,
643+
)
644+
}
645+
val mode = if (event.content.items.any { it.isVideo || (!it.isAudio && !it.isFile) }) {
646+
MediaViewerEntryPoint.MediaViewerMode.TimelineImagesAndVideos(timelineMode)
647+
} else {
648+
MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios(timelineMode)
649+
}
650+
NavTarget.MediaViewer(
651+
mode = mode,
652+
eventId = event.eventId,
653+
mediaInfo = mediaInfo,
654+
mediaSource = item.mediaSource,
655+
thumbnailSource = item.thumbnailSource,
656+
galleryItems = galleryItems,
657+
)
658+
}
601659
else -> null
602660
}
603661
return when (navTarget) {

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ class MessagesNode(
115115
)
116116

117117
interface Callback : Plugin {
118-
fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean
118+
fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event, galleryItemIndex: Int? = null): Boolean
119119
fun navigateToPreviewAttachments(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?)
120120
fun navigateToRoomMemberDetails(userId: UserId)
121121
fun handlePermalinkClick(data: PermalinkData)
@@ -278,6 +278,18 @@ class MessagesNode(
278278
}
279279
}
280280
},
281+
onGalleryItemClick = { isLive, event, index ->
282+
if (isLive) {
283+
callback.handleEventClick(timelineController.mainTimelineMode(), event, index)
284+
} else {
285+
val detachedTimelineMode = timelineController.detachedTimelineMode()
286+
if (detachedTimelineMode != null) {
287+
callback.handleEventClick(detachedTimelineMode, event, index)
288+
} else {
289+
false
290+
}
291+
}
292+
},
281293
onUserDataClick = callback::navigateToRoomMemberDetails,
282294
onLinkClick = { url, customTab ->
283295
onLinkClick(

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ fun MessagesView(
134134
onBackClick: () -> Unit,
135135
onRoomDetailsClick: () -> Unit,
136136
onEventContentClick: (isLive: Boolean, event: TimelineItem.Event) -> Boolean,
137+
onGalleryItemClick: ((isLive: Boolean, event: TimelineItem.Event, index: Int) -> Boolean)? = null,
137138
onUserDataClick: (UserId) -> Unit,
138139
onLinkClick: (String, Boolean) -> Unit,
139140
onSendLocationClick: () -> Unit,
@@ -256,6 +257,15 @@ fun MessagesView(
256257
MessagesViewContent(
257258
state = state,
258259
onContentClick = ::onContentClick,
260+
onGalleryItemClick = { evt, idx ->
261+
Timber.v("onGalleryItemClick= ${evt.id} index=$idx")
262+
val isLive = state.timelineState.isLive
263+
val handledByGallery = onGalleryItemClick?.invoke(isLive, evt, idx)
264+
val hideKeyboard = handledByGallery ?: onEventContentClick(isLive, evt)
265+
if (hideKeyboard) {
266+
localView.hideKeyboard()
267+
}
268+
},
259269
onMessageLongClick = ::onMessageLongClick,
260270
onUserDataClick = {
261271
hidingKeyboard {
@@ -451,6 +461,7 @@ private fun ReinviteDialog(state: MessagesState) {
451461
private fun MessagesViewContent(
452462
state: MessagesState,
453463
onContentClick: (TimelineItem.Event) -> Unit,
464+
onGalleryItemClick: ((TimelineItem.Event, Int) -> Unit)? = null,
454465
onUserDataClick: (MatrixUser) -> Unit,
455466
onLinkClick: (Link, Boolean) -> Unit,
456467
onReactionClick: (key: String, TimelineItem.Event) -> Unit,
@@ -510,6 +521,7 @@ private fun MessagesViewContent(
510521
onUserDataClick = onUserDataClick,
511522
onLinkClick = { link -> onLinkClick(link, false) },
512523
onContentClick = onContentClick,
524+
onGalleryItemClick = onGalleryItemClick,
513525
onMessageLongClick = onMessageLongClick,
514526
onSwipeToReply = onSwipeToReply,
515527
onReactionClick = onReactionClick,

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
6666
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
6767
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent
6868
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
69+
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemGalleryContent
6970
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
7071
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLegacyCallInviteContent
7172
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
@@ -294,6 +295,9 @@ private fun MessageSummary(
294295
is TimelineItemImageContent -> {
295296
content = { ContentForBody(event.content.bestDescription) }
296297
}
298+
is TimelineItemGalleryContent -> {
299+
content = { ContentForBody(event.content.body) }
300+
}
297301
is TimelineItemStickerContent -> {
298302
content = { ContentForBody(event.content.bestDescription) }
299303
}

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import io.element.android.libraries.matrix.api.core.EventId
3030
import io.element.android.libraries.matrix.api.core.SessionId
3131
import io.element.android.libraries.matrix.api.timeline.Timeline
3232
import io.element.android.libraries.mediaviewer.api.local.LocalMediaRenderer
33+
import kotlinx.collections.immutable.ImmutableList
3334

3435
@ContributesNode(RoomScope::class)
3536
@AssistedInject
@@ -42,7 +43,7 @@ class AttachmentsPreviewNode(
4243
private val enterpriseService: EnterpriseService,
4344
) : Node(buildContext, plugins = plugins) {
4445
data class Inputs(
45-
val attachment: Attachment,
46+
val attachments: ImmutableList<Attachment>,
4647
val timelineMode: Timeline.Mode,
4748
val inReplyToEventId: EventId?,
4849
) : NodeInputs
@@ -54,7 +55,7 @@ class AttachmentsPreviewNode(
5455
}
5556

5657
private val presenter = presenterFactory.create(
57-
attachment = inputs.attachment,
58+
attachments = inputs.attachments,
5859
timelineMode = inputs.timelineMode,
5960
onDoneListener = onDoneListener,
6061
inReplyToEventId = inputs.inReplyToEventId,

0 commit comments

Comments
 (0)