Skip to content

Commit

Permalink
[Feature] Render m.sticker events (#2122)
Browse files Browse the repository at this point in the history
* Render m.sticker events
---------

Signed-off-by: Marco Antonio Alvarez <[email protected]>
Co-authored-by: ElementBot <[email protected]>
  • Loading branch information
surakin and ElementBot authored Jan 2, 2024
1 parent 2134004 commit 87c8bc1
Show file tree
Hide file tree
Showing 150 changed files with 378 additions and 25 deletions.
1 change: 1 addition & 0 deletions changelog.d/1949.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Render m.sticker events
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
import io.element.android.features.poll.api.create.CreatePollEntryPoint
import io.element.android.features.poll.api.create.CreatePollMode
Expand Down Expand Up @@ -253,6 +254,19 @@ class MessagesFlowNode @AssistedInject constructor(
)
overlay.show(navTarget)
}
is TimelineItemStickerContent -> {
val navTarget = NavTarget.MediaViewer(
mediaInfo = MediaInfo(
name = event.content.body,
mimeType = event.content.mimeType,
formattedFileSize = event.content.formattedFileSize,
fileExtension = event.content.fileExtension
),
mediaSource = event.content.mediaSource,
thumbnailSource = event.content.thumbnailSource,
)
overlay.show(navTarget)
}
is TimelineItemVideoContent -> {
val navTarget = NavTarget.MediaViewer(
mediaInfo = MediaInfo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
Expand Down Expand Up @@ -351,6 +352,12 @@ class MessagesPresenter @AssistedInject constructor(
type = AttachmentThumbnailType.Image,
blurHash = targetEvent.content.blurhash,
)
is TimelineItemStickerContent -> AttachmentThumbnailInfo(
thumbnailSource = targetEvent.content.thumbnailSource ?: targetEvent.content.mediaSource,
textContent = targetEvent.content.body,
type = AttachmentThumbnailType.Image,
blurHash = targetEvent.content.blurhash,
)
is TimelineItemVideoContent -> AttachmentThumbnailInfo(
thumbnailSource = targetEvent.content.thumbnailSource,
textContent = targetEvent.content.body,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
Expand Down Expand Up @@ -239,6 +240,9 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif
is TimelineItemImageContent -> {
content = { ContentForBody(event.content.body) }
}
is TimelineItemStickerContent -> {
content = { ContentForBody(event.content.body) }
}
is TimelineItemVideoContent -> {
content = { ContentForBody(event.content.body) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import io.element.android.features.messages.impl.timeline.model.bubble.BubbleSta
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
Expand Down Expand Up @@ -583,6 +584,7 @@ private fun MessageEventBubbleContent(

val timestampPosition = when (event.content) {
is TimelineItemImageContent,
is TimelineItemStickerContent,
is TimelineItemVideoContent,
is TimelineItemLocationContent -> TimestampPosition.Overlay
is TimelineItemPollContent -> TimestampPosition.Below
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.MessageConten
import io.element.android.libraries.matrix.api.timeline.item.event.MessageType
import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
import io.element.android.libraries.matrix.api.timeline.item.event.StickerMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType
Expand Down Expand Up @@ -109,6 +110,10 @@ class InReplyToDetailsProvider : PreviewParameterProvider<InReplyToDetails> {
body = "Image",
type = ImageMessageType("Image", MediaSource("url"), null),
),
aMessageContent(
body = "Sticker",
type = StickerMessageType("Image", MediaSource("url"), null),
),
aMessageContent(
body = "File",
type = FileMessageType("File", MediaSource("url"), null),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
Expand Down Expand Up @@ -76,6 +77,10 @@ fun TimelineItemEventContentView(
content = content,
modifier = modifier,
)
is TimelineItemStickerContent -> TimelineItemStickerView(
content = content,
modifier = modifier,
)
is TimelineItemVideoContent -> TimelineItemVideoView(
content = content,
modifier = modifier
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.element.android.features.messages.impl.timeline.components.event

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.heightIn
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContentProvider
import io.element.android.libraries.designsystem.components.BlurHashAsyncImage
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.matrix.ui.media.MediaRequestData

private const val STICKER_SIZE_IN_DP = 128
private const val DEFAULT_ASPECT_RATIO = 1.33f

@Composable
fun TimelineItemStickerView(
content: TimelineItemStickerContent,
modifier: Modifier = Modifier,
) {
val safeAspectRatio = content.aspectRatio ?: DEFAULT_ASPECT_RATIO
Box(
modifier = modifier
.heightIn(min = STICKER_SIZE_IN_DP.dp, max = STICKER_SIZE_IN_DP.dp)
.aspectRatio(safeAspectRatio, false),
contentAlignment = Alignment.TopStart,
) {
BlurHashAsyncImage(
model = MediaRequestData(content.preferredMediaSource, MediaRequestData.Kind.File(content.body, content.mimeType)),
blurHash = content.blurhash,
)
}
}

@PreviewsDayNight
@Composable
internal fun TimelineItemStickerViewPreview(@PreviewParameter(TimelineItemStickerContentProvider::class) content: TimelineItemStickerContent) = ElementPreview {
TimelineItemStickerView(content)
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemNoticeContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
Expand All @@ -50,6 +51,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.MessageConten
import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat
import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.OtherMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.StickerMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType
Expand Down Expand Up @@ -93,6 +95,21 @@ class TimelineItemContentMessageFactory @Inject constructor(
fileExtension = fileExtensionExtractor.extractFromName(messageType.body)
)
}
is StickerMessageType -> {
val aspectRatio = aspectRatioOf(messageType.info?.width, messageType.info?.height)
TimelineItemStickerContent(
body = messageType.body,
mediaSource = messageType.source,
thumbnailSource = messageType.info?.thumbnailSource,
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
blurhash = messageType.info?.blurhash,
width = messageType.info?.width?.toInt(),
height = messageType.info?.height?.toInt(),
aspectRatio = aspectRatio,
formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0),
fileExtension = fileExtensionExtractor.extractFromName(messageType.body)
)
}
is LocationMessageType -> {
val location = Location.fromGeoUri(messageType.geoUri)
if (location == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,43 @@
package io.element.android.features.messages.impl.timeline.factories.event

import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContent
import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractor
import javax.inject.Inject

class TimelineItemContentStickerFactory @Inject constructor() {
class TimelineItemContentStickerFactory @Inject constructor(
private val fileSizeFormatter: FileSizeFormatter,
private val fileExtensionExtractor: FileExtensionExtractor
) {
private fun aspectRatioOf(width: Long?, height: Long?): Float? {
val result = if (height != null && width != null) {
width.toFloat() / height.toFloat()
} else {
null
}

fun create(@Suppress("UNUSED_PARAMETER") content: StickerContent): TimelineItemEventContent {
return TimelineItemUnknownContent
return result?.takeIf { it.isFinite() }
}

fun create(content: StickerContent): TimelineItemEventContent {

val aspectRatio = aspectRatioOf(content.info.width, content.info.height)

return TimelineItemStickerContent(
body = content.body,
mediaSource = MediaSource(content.url),
thumbnailSource = content.info.thumbnailSource,
mimeType = content.info.mimetype ?: MimeTypes.OctetStream,
blurhash = content.info.blurhash,
width = content.info.width?.toInt(),
height = content.info.height?.toInt(),
aspectRatio = aspectRatio,
formattedFileSize = fileSizeFormatter.format(content.info.size ?: 0),
fileExtension = fileExtensionExtractor.extractFromName(content.body)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRoomMembershipContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
Expand Down Expand Up @@ -54,6 +55,7 @@ internal fun TimelineItem.Event.canBeGrouped(): Boolean {
is TimelineItemTextBasedContent,
is TimelineItemEncryptedContent,
is TimelineItemImageContent,
is TimelineItemStickerContent,
is TimelineItemFileContent,
is TimelineItemVideoContent,
is TimelineItemAudioContent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.timeline.item.event.EventContent
import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
import io.element.android.libraries.matrix.ui.messages.toPlainText

Expand All @@ -45,6 +46,10 @@ fun InReplyTo.map() = when (this) {
val messageContent = content as MessageContent
(messageContent.type as? TextMessageType)?.toPlainText() ?: messageContent.body
}
is StickerContent -> {
val stickerContent = content as StickerContent
stickerContent.body
}
else -> null
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ package io.element.android.features.messages.impl.timeline.model
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.ui.res.stringResource
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
Expand Down Expand Up @@ -98,6 +100,13 @@ internal fun InReplyToDetails.metadata(): InReplyToMetadata? = when (eventConten
)
else -> InReplyToMetadata.Text(textContent ?: eventContent.body)
}
is StickerContent -> InReplyToMetadata.Thumbnail(
AttachmentThumbnailInfo(
thumbnailSource = MediaSource(eventContent.url),
textContent = eventContent.body,
type = AttachmentThumbnailType.Image
)
)
is PollContent -> InReplyToMetadata.Thumbnail(
AttachmentThumbnailInfo(
textContent = eventContent.question,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ fun TimelineItemEventContent.canReact(): Boolean =
is TimelineItemEncryptedContent,
is TimelineItemFileContent,
is TimelineItemImageContent,
is TimelineItemStickerContent,
is TimelineItemLocationContent,
is TimelineItemPollContent,
is TimelineItemVoiceContent,
Expand Down
Loading

0 comments on commit 87c8bc1

Please sign in to comment.