From fb85f35525659c503a0a77f1de9c536fac4671c9 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 10 Mar 2023 18:32:46 +0100 Subject: [PATCH] [MatrixSDK] start mapping all the timeline objects --- .../TimelineItemContentMessageFactory.kt | 2 +- .../TimelineItemDaySeparatorFactory.kt | 4 +- .../virtual/TimelineItemVirtualFactory.kt | 9 +- .../event/TimelineItemImageContentProvider.kt | 2 +- .../libraries/matrix/api/media/AudioInfo.kt | 22 ++ .../libraries/matrix/api/media/FileInfo.kt | 24 ++ .../libraries/matrix/api/media/ImageInfo.kt | 27 +++ .../matrix/api/media/MediaResolver.kt | 5 +- .../matrix/api/media/ThumbnailInfo.kt | 24 ++ .../libraries/matrix/api/media/VideoInfo.kt | 28 +++ .../matrix/api/timeline/MatrixTimelineItem.kt | 8 +- .../api/timeline/item/event/EventReaction.kt | 22 ++ .../api/timeline/item/event/EventSendState.kt | 31 +++ .../timeline/item/event/EventTimelineItem.kt | 35 +++ .../item/event/ProfileTimelineDetails.kt | 33 +++ .../item/event/TimelineEventContent.kt | 212 ++++++++++++++++++ .../item/virtual/VirtualTimelineItem.kt | 30 +++ .../libraries/matrix/impl/media/AudioInfo.kt | 25 +++ .../libraries/matrix/impl/media/FileInfo.kt | 29 +++ .../libraries/matrix/impl/media/ImageInfo.kt | 30 +++ .../MediaResolver.kt} | 28 ++- .../matrix/impl/media/MediaSource.kt | 22 ++ .../matrix/impl/media/RustMediaResolver.kt | 14 +- .../matrix/impl/media/ThumbnailInfo.kt | 27 +++ .../libraries/matrix/impl/media/VideoInfo.kt | 31 +++ .../timeline/MatrixTimelineDiffProcessor.kt | 9 +- .../impl/timeline/MatrixTimelineItemMapper.kt | 42 ++++ .../impl/timeline/RustMatrixTimeline.kt | 20 +- .../timeline/item/event/EventMessageMapper.kt | 89 ++++++++ .../item/event/EventTimelineItemMapper.kt | 80 +++++++ .../item/event/TimelineEventContentMapper.kt | 90 ++++++++ .../item/virtual/VirtualTimelineItemMapper.kt | 32 +++ .../matrix/impl/util/TaskHandleBag.kt | 5 +- .../matrix/test/media/FakeMediaResolver.kt | 4 - .../matrix/ui/media/AvatarDataExt.kt | 4 +- .../libraries/matrix/ui/media/MediaFetcher.kt | 2 +- .../libraries/matrix/ui/media/MediaKeyer.kt | 2 +- 37 files changed, 1054 insertions(+), 49 deletions(-) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioInfo.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/FileInfo.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/ImageInfo.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/ThumbnailInfo.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/VideoInfo.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventReaction.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventSendState.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ProfileTimelineDetails.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/TimelineEventContent.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/virtual/VirtualTimelineItem.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioInfo.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/FileInfo.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt rename libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/{timeline/MatrixTimelineItem.kt => media/MediaResolver.kt} (54%) create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/MediaSource.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ThumbnailInfo.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/VideoInfo.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItemMapper.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/virtual/VirtualTimelineItemMapper.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index 0f9f216699..3146d24a10 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -47,7 +47,7 @@ class TimelineItemContentMessageFactory @Inject constructor() { TimelineItemImageContent( body = messageType.content.body, imageMeta = MediaResolver.Meta( - source = messageType.content.source, + url = messageType.content.source, kind = MediaResolver.Kind.Content ), blurhash = messageType.content.info?.blurhash, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemDaySeparatorFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemDaySeparatorFactory.kt index 5192f190db..5a778fe28a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemDaySeparatorFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemDaySeparatorFactory.kt @@ -19,13 +19,13 @@ package io.element.android.features.messages.impl.timeline.factories.virtual import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemDaySeparatorModel import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemVirtualModel import io.element.android.libraries.dateformatter.api.DaySeparatorFormatter -import org.matrix.rustcomponents.sdk.VirtualTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem import javax.inject.Inject class TimelineItemDaySeparatorFactory @Inject constructor(private val daySeparatorFormatter: DaySeparatorFormatter) { fun create(virtualItem: VirtualTimelineItem.DayDivider): TimelineItemVirtualModel { - val formattedDate = daySeparatorFormatter.format(virtualItem.ts.toLong()) + val formattedDate = daySeparatorFormatter.format(virtualItem.timestamp) return TimelineItemDaySeparatorModel( formattedDate = formattedDate ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemVirtualFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemVirtualFactory.kt index b71a911086..a6ee1de9b2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemVirtualFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemVirtualFactory.kt @@ -22,6 +22,7 @@ import io.element.android.features.messages.impl.timeline.model.virtual.Timeline import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemUnknownVirtualModel import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemVirtualModel import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem import org.matrix.rustcomponents.sdk.VirtualTimelineItem import javax.inject.Inject @@ -42,10 +43,10 @@ class TimelineItemVirtualFactory @Inject constructor( private fun MatrixTimelineItem.Virtual.computeModel(index: Int): TimelineItemVirtualModel { return when (val inner = virtual) { - is VirtualTimelineItem.DayDivider -> daySeparatorFactory.create(inner) - is VirtualTimelineItem.ReadMarker -> TimelineItemReadMarkerModel - is VirtualTimelineItem.LoadingIndicator -> TimelineItemLoadingModel - is VirtualTimelineItem.TimelineStart -> TimelineItemReadMarkerModel + is io.element.android.libraries.matrix.api.timeline.item.virtual.TimelineItemVirtual.VirtualTimelineItem.DayDivider -> daySeparatorFactory.create(inner) + is io.element.android.libraries.matrix.api.timeline.item.virtual.TimelineItemVirtual.VirtualTimelineItem.ReadMarker -> TimelineItemReadMarkerModel + is io.element.android.libraries.matrix.api.timeline.item.virtual.TimelineItemVirtual.VirtualTimelineItem.LoadingIndicator -> TimelineItemLoadingModel + is io.element.android.libraries.matrix.api.timeline.item.virtual.TimelineItemVirtual.VirtualTimelineItem.TimelineStart -> TimelineItemReadMarkerModel else -> TimelineItemUnknownVirtualModel } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt index 3aa9e4902f..ba79c9988f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt @@ -30,7 +30,7 @@ open class TimelineItemImageContentProvider : PreviewParameterProvider, + val sender: UserId, + val senderProfile: ProfileTimelineDetails, + val timestamp: Long, + val content: TimelineEventContent +) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ProfileTimelineDetails.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ProfileTimelineDetails.kt new file mode 100644 index 0000000000..fa22d3cf54 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ProfileTimelineDetails.kt @@ -0,0 +1,33 @@ +/* + * 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.libraries.matrix.api.timeline.item.event + +sealed interface ProfileTimelineDetails { + object Unavailable : ProfileTimelineDetails + + object Pending : ProfileTimelineDetails + + data class Ready( + val displayName: String?, + val displayNameAmbiguous: Boolean, + val avatarUrl: String? + ) : ProfileTimelineDetails + + data class Error( + val message: String + ) : ProfileTimelineDetails +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/TimelineEventContent.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/TimelineEventContent.kt new file mode 100644 index 0000000000..4fc2f437f7 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/TimelineEventContent.kt @@ -0,0 +1,212 @@ +/* + * 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.libraries.matrix.api.timeline.item.event + +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.media.AudioInfo +import io.element.android.libraries.matrix.api.media.FileInfo +import io.element.android.libraries.matrix.api.media.ImageInfo +import io.element.android.libraries.matrix.api.media.VideoInfo + +sealed interface TimelineEventContent + +data class TimelineEventMessageContent( + val body: String, + val inReplyTo: UserId?, + val isEdited: Boolean, + val content: MessageContent? +) : TimelineEventContent + +object RedactedContent : TimelineEventContent + +data class StickerContent( + val body: String, + val info: ImageInfo, + val url: String +) : TimelineEventContent + +sealed interface EncryptedMessage { + data class OlmV1Curve25519AesSha2( + val senderKey: String + ) : EncryptedMessage + + data class MegolmV1AesSha2( + val sessionId: String + ) : EncryptedMessage + + object Unknown : EncryptedMessage +} + +data class UnableToDecryptContent( + val message: EncryptedMessage +) : TimelineEventContent + +data class RoomMembership( + val userId: UserId, + val change: MembershipChange? +) : TimelineEventContent + +data class ProfileChange( + val displayName: String?, + val prevDisplayName: String?, + val avatarUrl: String?, + val prevAvatarUrl: String? +) : TimelineEventContent + +data class State( + val stateKey: String, + val content: OtherState +) : TimelineEventContent + +data class FailedToParseMessageLike( + val eventType: String, + val error: String +) : TimelineEventContent + +data class FailedToParseState( + val eventType: String, + val stateKey: String, + val error: String +) : TimelineEventContent + +object UnknownContent : TimelineEventContent + +sealed interface MessageContent + +object UnknownMessageContent : MessageContent + +enum class MessageFormat { + HTML, UNKNOWN +} + +data class FormattedBody( + val format: MessageFormat, + val body: String +) + +data class EmoteMessageContent( + val body: String, + val formatted: FormattedBody? +) : MessageContent + +data class ImageMessageContent( + val body: String, + val url: String, + val info: ImageInfo? +) : MessageContent + +data class AudioMessageContent( + var body: String, + var url: String, + var info: AudioInfo? +) : MessageContent + +data class VideoMessageContent( + val body: String, + val url: String, + val info: VideoInfo? +) : MessageContent + +data class FileMessageContent( + val body: String, + val url: String, + val info: FileInfo? +) : MessageContent + +data class NoticeMessageContent( + val body: String, + val formatted: FormattedBody? +) : MessageContent + +data class TextMessageContent( + val body: String, + val formatted: FormattedBody? +) : MessageContent + +enum class MembershipChange { + NONE, + ERROR, + JOINED, + LEFT, + BANNED, + UNBANNED, + KICKED, + INVITED, + KICKED_AND_BANNED, + INVITATION_ACCEPTED, + INVITATION_REJECTED, + INVITATION_REVOKED, + KNOCKED, + KNOCK_ACCEPTED, + KNOCK_RETRACTED, + KNOCK_DENIED, + NOT_IMPLEMENTED; +} + +sealed interface OtherState { + object PolicyRuleRoom : OtherState + + object PolicyRuleServer : OtherState + + object PolicyRuleUser : OtherState + + object RoomAliases : OtherState + + data class RoomAvatar( + val url: String? + ) : OtherState + + object RoomCanonicalAlias : OtherState + + object RoomCreate : OtherState + + object RoomEncryption : OtherState + + object RoomGuestAccess : OtherState + + object RoomHistoryVisibility : OtherState + + object RoomJoinRules : OtherState + + data class RoomName( + val name: String? + ) : OtherState + + object RoomPinnedEvents : OtherState + + object RoomPowerLevels : OtherState + + object RoomServerAcl : OtherState + + data class RoomThirdPartyInvite( + val displayName: String? + ) : OtherState + + object RoomTombstone : OtherState + + data class RoomTopic( + val `topic`: String? + ) : OtherState + + object SpaceChild : OtherState + + object SpaceParent : OtherState + + data class Custom( + val eventType: String + ) : OtherState +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/virtual/VirtualTimelineItem.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/virtual/VirtualTimelineItem.kt new file mode 100644 index 0000000000..9a700ac98b --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/virtual/VirtualTimelineItem.kt @@ -0,0 +1,30 @@ +/* + * 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.libraries.matrix.api.timeline.item.virtual + +sealed interface VirtualTimelineItem { + + data class DayDivider( + val timestamp: Long + ) : VirtualTimelineItem + + object ReadMarker : VirtualTimelineItem + + object LoadingIndicator : VirtualTimelineItem + + object TimelineStart : VirtualTimelineItem +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioInfo.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioInfo.kt new file mode 100644 index 0000000000..1108dd1cc8 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioInfo.kt @@ -0,0 +1,25 @@ +/* + * 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.libraries.matrix.impl.media + +import io.element.android.libraries.matrix.api.media.AudioInfo +import org.matrix.rustcomponents.sdk.AudioInfo as RustAudioInfo + +fun RustAudioInfo.map(): AudioInfo = AudioInfo( + duration = duration?.toLong(), + size = size?.toLong() +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/FileInfo.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/FileInfo.kt new file mode 100644 index 0000000000..98c96c4d9a --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/FileInfo.kt @@ -0,0 +1,29 @@ +/* + * 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.libraries.matrix.impl.media + +import io.element.android.libraries.matrix.api.media.FileInfo +import io.element.android.libraries.matrix.api.media.ThumbnailInfo +import org.matrix.rustcomponents.sdk.FileInfo as RustFileInfo +import org.matrix.rustcomponents.sdk.ThumbnailInfo as RustThumbnailInfo + +fun RustFileInfo.map(): FileInfo = FileInfo( + mimetype = mimetype, + size = size?.toLong(), + thumbnailInfo = thumbnailInfo?.map(), + thumbnailUrl = thumbnailSource?.useUrl() +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt new file mode 100644 index 0000000000..27ab6d656a --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt @@ -0,0 +1,30 @@ +/* + * 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.libraries.matrix.impl.media + +import io.element.android.libraries.matrix.api.media.ImageInfo +import org.matrix.rustcomponents.sdk.ImageInfo as RustImageInfo + +fun RustImageInfo.map(): ImageInfo = ImageInfo( + height = height?.toLong(), + width = width?.toLong(), + mimetype = mimetype, + size = size?.toLong(), + thumbnailInfo = thumbnailInfo?.map(), + thumbnailUrl = thumbnailSource?.useUrl(), + blurhash = blurhash +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItem.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/MediaResolver.kt similarity index 54% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItem.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/MediaResolver.kt index 70cbc165f7..ca840ee44f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItem.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/MediaResolver.kt @@ -14,19 +14,23 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.impl.timeline +package io.element.android.libraries.matrix.impl.media -import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem -import org.matrix.rustcomponents.sdk.TimelineItem +interface MediaResolver { -fun TimelineItem.asMatrixTimelineItem(): MatrixTimelineItem { - val asEvent = asEvent() - if (asEvent != null) { - return MatrixTimelineItem.Event(asEvent) + sealed interface Kind { + data class Thumbnail(val width: Int, val height: Int) : Kind { + constructor(size: Int) : this(size, size) + } + + object Content : Kind } - val asVirtual = asVirtual() - if (asVirtual != null) { - return MatrixTimelineItem.Virtual(asVirtual) - } - return MatrixTimelineItem.Other + + data class Meta( + val url: String?, + val kind: Kind + ) + + suspend fun resolve(url: String?, kind: Kind): ByteArray? + } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/MediaSource.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/MediaSource.kt new file mode 100644 index 0000000000..2fc50611e8 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/MediaSource.kt @@ -0,0 +1,22 @@ +/* + * 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.libraries.matrix.impl.media + +import org.matrix.rustcomponents.sdk.MediaSource +import org.matrix.rustcomponents.sdk.use + +fun MediaSource.useUrl(): String = use { it.url() } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaResolver.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaResolver.kt index 413ee26f67..8b16b8e551 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaResolver.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaResolver.kt @@ -18,23 +18,23 @@ package io.element.android.libraries.matrix.impl.media import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.media.MediaResolver +import org.matrix.rustcomponents.sdk.MediaSource import org.matrix.rustcomponents.sdk.mediaSourceFromUrl internal class RustMediaResolver(private val client: MatrixClient) : MediaResolver { override suspend fun resolve(url: String?, kind: MediaResolver.Kind): ByteArray? { if (url.isNullOrEmpty()) return null - val mediaSource = mediaSourceFromUrl(url) - return resolve(MediaResolver.Meta(mediaSource, kind)) + return mediaSourceFromUrl(url).use { mediaSource -> + resolve(mediaSource, kind) + } } - override suspend fun resolve(meta: MediaResolver.Meta): ByteArray? { - val source = meta.source ?: return null - val kind = meta.kind + private suspend fun resolve(mediaSource: MediaSource, kind: MediaResolver.Kind): ByteArray? { return when (kind) { - is MediaResolver.Kind.Content -> client.loadMediaContentForSource(source) + is MediaResolver.Kind.Content -> client.loadMediaContentForSource(mediaSource) is MediaResolver.Kind.Thumbnail -> client.loadMediaThumbnailForSource( - source, + mediaSource, kind.width.toLong(), kind.height.toLong() ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ThumbnailInfo.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ThumbnailInfo.kt new file mode 100644 index 0000000000..3f10720132 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ThumbnailInfo.kt @@ -0,0 +1,27 @@ +/* + * 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.libraries.matrix.impl.media + +import io.element.android.libraries.matrix.api.media.ThumbnailInfo +import org.matrix.rustcomponents.sdk.ThumbnailInfo as RustThumbnailInfo + +fun RustThumbnailInfo.map(): ThumbnailInfo = ThumbnailInfo( + height = height?.toLong(), + width = width?.toLong(), + mimetype = mimetype, + size = size?.toLong() +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/VideoInfo.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/VideoInfo.kt new file mode 100644 index 0000000000..0db364f544 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/VideoInfo.kt @@ -0,0 +1,31 @@ +/* + * 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.libraries.matrix.impl.media + +import io.element.android.libraries.matrix.api.media.VideoInfo +import org.matrix.rustcomponents.sdk.VideoInfo as RustVideoInfo + +fun RustVideoInfo.map(): VideoInfo = VideoInfo( + duration = duration?.toLong(), + height = height?.toLong(), + width = width?.toLong(), + mimetype = mimetype, + size = size?.toLong(), + thumbnailInfo = thumbnailInfo?.map(), + thumbnailUrl = thumbnailSource?.useUrl(), + blurhash = blurhash +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineDiffProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineDiffProcessor.kt index f3033a40e1..7d299519a5 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineDiffProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineDiffProcessor.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.timeline import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow @@ -25,14 +26,15 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.TimelineChange import org.matrix.rustcomponents.sdk.TimelineDiff +import org.matrix.rustcomponents.sdk.TimelineItem import org.matrix.rustcomponents.sdk.TimelineListener -import org.matrix.rustcomponents.sdk.VirtualTimelineItem internal class MatrixTimelineDiffProcessor( private val paginationState: MutableStateFlow, private val timelineItems: MutableStateFlow>, private val coroutineScope: CoroutineScope, private val diffDispatcher: CoroutineDispatcher, + private val timelineItemFactory: MatrixTimelineItemMapper, ) : TimelineListener { override fun onUpdate(update: TimelineDiff) { @@ -117,4 +119,9 @@ internal class MatrixTimelineDiffProcessor( } } } + + private fun TimelineItem.asMatrixTimelineItem(): MatrixTimelineItem { + return timelineItemFactory.map(this) + } + } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItemMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItemMapper.kt new file mode 100644 index 0000000000..7151f3550b --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItemMapper.kt @@ -0,0 +1,42 @@ +/* + * 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.libraries.matrix.impl.timeline + +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper +import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper +import org.matrix.rustcomponents.sdk.TimelineItem + +class MatrixTimelineItemMapper( + private val virtualTimelineItemMapper: VirtualTimelineItemMapper = VirtualTimelineItemMapper(), + private val eventTimelineItemMapper: EventTimelineItemMapper= EventTimelineItemMapper(), +) { + + fun map(timelineItem: TimelineItem): MatrixTimelineItem = timelineItem.use { + val asEvent = timelineItem.asEvent() + if (asEvent != null) { + val eventTimelineItem = eventTimelineItemMapper.map(asEvent) + return MatrixTimelineItem.Event(eventTimelineItem) + } + val asVirtual = timelineItem.asVirtual() + if (asVirtual != null) { + val virtualTimelineItem = virtualTimelineItemMapper.map(asVirtual) + return MatrixTimelineItem.Virtual(virtualTimelineItem) + } + return MatrixTimelineItem.Other + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt index 98ea6e7d3f..4e4c19458d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt @@ -17,11 +17,15 @@ package io.element.android.libraries.matrix.impl.timeline import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.matrix.impl.util.TaskHandleBag import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.impl.timeline.item.event.EventMessageMapper +import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper +import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper +import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper +import io.element.android.libraries.matrix.impl.util.TaskHandleBag import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow @@ -52,11 +56,21 @@ class RustMatrixTimeline( MatrixTimeline.PaginationState(canBackPaginate = true, isBackPaginating = false) ) + private val timelineItemFactory = MatrixTimelineItemMapper( + virtualTimelineItemMapper = VirtualTimelineItemMapper(), + eventTimelineItemMapper = EventTimelineItemMapper( + contentMapper = TimelineEventContentMapper( + eventMessageMapper = EventMessageMapper() + ) + ) + ) + private val innerTimelineListener = MatrixTimelineDiffProcessor( paginationState = paginationState, timelineItems = timelineItems, coroutineScope = coroutineScope, - diffDispatcher = coroutineDispatchers.diffUpdateDispatcher + diffDispatcher = coroutineDispatchers.diffUpdateDispatcher, + timelineItemFactory = timelineItemFactory, ) private val listenerTokens = TaskHandleBag() @@ -83,7 +97,7 @@ class RustMatrixTimeline( val result = addListener(innerTimelineListener) result .onSuccess { timelineItems -> - val matrixTimelineItems = timelineItems.map { it.asMatrixTimelineItem() } + val matrixTimelineItems = timelineItems.map(timelineItemFactory::map) withContext(coroutineDispatchers.diffUpdateDispatcher) { this@RustMatrixTimeline.timelineItems.value = matrixTimelineItems } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt new file mode 100644 index 0000000000..6cf2e7878f --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt @@ -0,0 +1,89 @@ +/* + * 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.libraries.matrix.impl.timeline.item.event + +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody +import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat +import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.TimelineEventMessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageContent +import io.element.android.libraries.matrix.impl.media.map +import io.element.android.libraries.matrix.impl.media.useUrl +import org.matrix.rustcomponents.sdk.Message +import org.matrix.rustcomponents.sdk.MessageType +import org.matrix.rustcomponents.sdk.use +import org.matrix.rustcomponents.sdk.FormattedBody as RustFormattedBody +import org.matrix.rustcomponents.sdk.MessageFormat as RustMessageFormat + +class EventMessageMapper { + + fun map(message: Message): TimelineEventMessageContent = message.use { + val content = message.msgtype().use { type -> + when (type) { + is MessageType.Audio -> { + AudioMessageContent(type.content.body, type.content.source.useUrl(), type.content.info?.map()) + } + is MessageType.File -> { + FileMessageContent(type.content.body, type.content.source.useUrl(), type.content.info?.map()) + } + is MessageType.Image -> { + ImageMessageContent(type.content.body, type.content.source.useUrl(), type.content.info?.map()) + } + is MessageType.Notice -> { + NoticeMessageContent(type.content.body, type.content.formatted?.map()) + } + is MessageType.Text -> { + TextMessageContent(type.content.body, type.content.formatted?.map()) + } + is MessageType.Emote -> { + EmoteMessageContent(type.content.body, type.content.formatted?.map()) + } + is MessageType.Video -> { + VideoMessageContent(type.content.body, type.content.source.useUrl(), type.content.info?.map()) + } + null -> { + UnknownMessageContent + } + } + } + TimelineEventMessageContent( + body = message.body(), + inReplyTo = message.inReplyTo()?.let { UserId(it) }, + isEdited = message.isEdited(), + content = content + ) + } +} + +private fun RustFormattedBody.map(): FormattedBody = FormattedBody( + format = format.map(), + body = body +) + +private fun RustMessageFormat.map(): MessageFormat { + return when (this) { + RustMessageFormat.HTML -> MessageFormat.HTML + RustMessageFormat.UNKNOWN -> MessageFormat.UNKNOWN + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt new file mode 100644 index 0000000000..c399dd0b1d --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt @@ -0,0 +1,80 @@ +/* + * 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.libraries.matrix.impl.timeline.item.event + +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.timeline.item.event.EventReaction +import io.element.android.libraries.matrix.api.timeline.item.event.EventSendState +import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails +import io.element.android.libraries.matrix.api.timeline.item.event.TimelineEventContent +import org.matrix.rustcomponents.sdk.Reaction +import org.matrix.rustcomponents.sdk.EventSendState as RustEventSendState +import org.matrix.rustcomponents.sdk.EventTimelineItem as RustEventTimelineItem +import org.matrix.rustcomponents.sdk.ProfileTimelineDetails as RustProfileTimelineDetails + +class EventTimelineItemMapper(private val contentMapper: TimelineEventContentMapper = TimelineEventContentMapper()) { + + fun map(eventTimelineItem: RustEventTimelineItem): EventTimelineItem = eventTimelineItem.use { + EventTimelineItem( + uniqueIdentifier = eventTimelineItem.uniqueIdentifier(), + eventId = eventTimelineItem.eventId()?.let { EventId(it) }, + isEditable = eventTimelineItem.isEditable(), + isLocal = eventTimelineItem.isLocal(), + isOwn = eventTimelineItem.isOwn(), + isRemote = eventTimelineItem.isRemote(), + localSendState = eventTimelineItem.localSendState()?.map(), + reactions = eventTimelineItem.reactions().map(), + sender = UserId(eventTimelineItem.sender()), + senderProfile = eventTimelineItem.senderProfile().map(), + timestamp = eventTimelineItem.timestamp().toLong(), + content = contentMapper.map(eventTimelineItem.content()) + ) + } +} + +fun RustProfileTimelineDetails.map(): ProfileTimelineDetails { + return when (this) { + RustProfileTimelineDetails.Pending -> ProfileTimelineDetails.Pending + RustProfileTimelineDetails.Unavailable -> ProfileTimelineDetails.Unavailable + is RustProfileTimelineDetails.Error -> ProfileTimelineDetails.Error(message) + is RustProfileTimelineDetails.Ready -> ProfileTimelineDetails.Ready( + displayName = displayName, + displayNameAmbiguous = displayNameAmbiguous, + avatarUrl = avatarUrl + ) + } +} + +fun RustEventSendState?.map(): EventSendState? { + return when (this) { + null -> null + RustEventSendState.NotSendYet -> EventSendState.NotSendYet + is RustEventSendState.SendingFailed -> EventSendState.SendingFailed(error) + is RustEventSendState.Sent -> EventSendState.Sent(EventId(eventId)) + } +} + +private fun List?.map(): List { + return this?.map { + EventReaction( + key = it.key, + count = it.count.toLong() + ) + } ?: emptyList() +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt new file mode 100644 index 0000000000..e40e7f82d5 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt @@ -0,0 +1,90 @@ +/* + * 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.libraries.matrix.impl.timeline.item.event + +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLike +import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseState +import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange +import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChange +import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent +import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembership +import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent +import io.element.android.libraries.matrix.api.timeline.item.event.TimelineEventContent +import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent +import io.element.android.libraries.matrix.impl.media.map +import org.matrix.rustcomponents.sdk.TimelineItemContent +import org.matrix.rustcomponents.sdk.TimelineItemContentKind + +class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMapper = EventMessageMapper()) { + + fun map(content: TimelineItemContent): TimelineEventContent = content.use { + when (val kind = content.kind()) { + is TimelineItemContentKind.FailedToParseMessageLike -> { + FailedToParseMessageLike( + eventType = kind.eventType, + error = kind.error + ) + } + is TimelineItemContentKind.FailedToParseState -> { + FailedToParseState( + eventType = kind.eventType, + stateKey = kind.stateKey, + error = kind.error + ) + } + TimelineItemContentKind.Message -> { + val message = content.asMessage() + if (message == null) { + UnknownContent + } else { + eventMessageMapper.map(message) + } + } + is TimelineItemContentKind.ProfileChange -> { + ProfileChange( + displayName = kind.displayName, + prevDisplayName = kind.prevDisplayName, + avatarUrl = kind.avatarUrl, + prevAvatarUrl = kind.prevAvatarUrl + ) + } + TimelineItemContentKind.RedactedMessage -> { + RedactedContent + } + is TimelineItemContentKind.RoomMembership -> { + RoomMembership( + UserId(kind.userId), + MembershipChange.JOINED + ) + } + is TimelineItemContentKind.State -> { + UnknownContent + } + is TimelineItemContentKind.Sticker -> { + StickerContent( + body = kind.body, + info = kind.info.map(), + url = kind.url + ) + } + is TimelineItemContentKind.UnableToDecrypt -> { + UnknownContent + } + } + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/virtual/VirtualTimelineItemMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/virtual/VirtualTimelineItemMapper.kt new file mode 100644 index 0000000000..8e2dd9304f --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/virtual/VirtualTimelineItemMapper.kt @@ -0,0 +1,32 @@ +/* + * 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.libraries.matrix.impl.timeline.item.virtual + +import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem +import org.matrix.rustcomponents.sdk.VirtualTimelineItem as RustVirtualTimelineItem + +class VirtualTimelineItemMapper { + + fun map(virtualTimelineItem: RustVirtualTimelineItem): VirtualTimelineItem { + return when (virtualTimelineItem) { + is RustVirtualTimelineItem.DayDivider -> VirtualTimelineItem.DayDivider(virtualTimelineItem.ts.toLong()) + RustVirtualTimelineItem.LoadingIndicator -> VirtualTimelineItem.LoadingIndicator + RustVirtualTimelineItem.ReadMarker -> VirtualTimelineItem.ReadMarker + RustVirtualTimelineItem.TimelineStart -> VirtualTimelineItem.TimelineStart + } + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandleBag.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandleBag.kt index ad2c6ccddd..9a21645351 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandleBag.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandleBag.kt @@ -27,7 +27,10 @@ class TaskHandleBag(private val tokens: MutableSet = CopyOnWriteArra } fun dispose() { - tokens.forEach { it.cancel() } + tokens.forEach { + it.cancel() + it.destroy() + } tokens.clear() } } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaResolver.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaResolver.kt index c642c630f5..4d5ebc8029 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaResolver.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaResolver.kt @@ -22,8 +22,4 @@ class FakeMediaResolver : MediaResolver { override suspend fun resolve(url: String?, kind: MediaResolver.Kind): ByteArray? { return null } - - override suspend fun resolve(meta: MediaResolver.Meta): ByteArray? { - return null - } } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataExt.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataExt.kt index f12842eff0..1392a1ff5c 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataExt.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataExt.kt @@ -18,9 +18,7 @@ package io.element.android.libraries.matrix.ui.media import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.matrix.api.media.MediaResolver -import org.matrix.rustcomponents.sdk.mediaSourceFromUrl fun AvatarData.toMetadata(): MediaResolver.Meta { - val mediaSource = url?.let { mediaSourceFromUrl(it) } - return MediaResolver.Meta(source = mediaSource, kind = MediaResolver.Kind.Thumbnail(size.value)) + return MediaResolver.Meta(url = url, kind = MediaResolver.Kind.Thumbnail(size.value)) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaFetcher.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaFetcher.kt index fa0f42bf9b..6567101162 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaFetcher.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaFetcher.kt @@ -33,7 +33,7 @@ internal class MediaFetcher( ) : Fetcher { override suspend fun fetch(): FetchResult? { - val byteArray = mediaResolver?.resolve(meta) ?: return null + val byteArray = mediaResolver?.resolve(meta.url, meta.kind) ?: return null val byteBuffer = ByteBuffer.wrap(byteArray) return imageLoader.components.newFetcher(byteBuffer, options, imageLoader)?.first?.fetch() } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaKeyer.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaKeyer.kt index 7861649afe..2d4ab683b1 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaKeyer.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaKeyer.kt @@ -33,4 +33,4 @@ internal class MediaKeyer : Keyer { } } -private fun MediaResolver.Meta.toKey() = "${source?.url()}_${kind}" +private fun MediaResolver.Meta.toKey() = "${url}_${kind}"