[MatrixSDK] start mapping all the timeline objects

This commit is contained in:
ganfra 2023-03-10 18:32:46 +01:00
parent b1c116b883
commit 8ccd21d856
37 changed files with 1054 additions and 49 deletions

View file

@ -47,7 +47,7 @@ class TimelineItemContentMessageFactory @Inject constructor() {
TimelineItemImageContent( TimelineItemImageContent(
body = messageType.content.body, body = messageType.content.body,
imageMeta = MediaResolver.Meta( imageMeta = MediaResolver.Meta(
source = messageType.content.source, url = messageType.content.source,
kind = MediaResolver.Kind.Content kind = MediaResolver.Kind.Content
), ),
blurhash = messageType.content.info?.blurhash, blurhash = messageType.content.info?.blurhash,

View file

@ -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.TimelineItemDaySeparatorModel
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemVirtualModel import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemVirtualModel
import io.element.android.libraries.dateformatter.api.DaySeparatorFormatter 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 import javax.inject.Inject
class TimelineItemDaySeparatorFactory @Inject constructor(private val daySeparatorFormatter: DaySeparatorFormatter) { class TimelineItemDaySeparatorFactory @Inject constructor(private val daySeparatorFormatter: DaySeparatorFormatter) {
fun create(virtualItem: VirtualTimelineItem.DayDivider): TimelineItemVirtualModel { fun create(virtualItem: VirtualTimelineItem.DayDivider): TimelineItemVirtualModel {
val formattedDate = daySeparatorFormatter.format(virtualItem.ts.toLong()) val formattedDate = daySeparatorFormatter.format(virtualItem.timestamp)
return TimelineItemDaySeparatorModel( return TimelineItemDaySeparatorModel(
formattedDate = formattedDate formattedDate = formattedDate
) )

View file

@ -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.TimelineItemUnknownVirtualModel
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemVirtualModel 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.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
import org.matrix.rustcomponents.sdk.VirtualTimelineItem import org.matrix.rustcomponents.sdk.VirtualTimelineItem
import javax.inject.Inject import javax.inject.Inject
@ -42,10 +43,10 @@ class TimelineItemVirtualFactory @Inject constructor(
private fun MatrixTimelineItem.Virtual.computeModel(index: Int): TimelineItemVirtualModel { private fun MatrixTimelineItem.Virtual.computeModel(index: Int): TimelineItemVirtualModel {
return when (val inner = virtual) { return when (val inner = virtual) {
is VirtualTimelineItem.DayDivider -> daySeparatorFactory.create(inner) is io.element.android.libraries.matrix.api.timeline.item.virtual.TimelineItemVirtual.VirtualTimelineItem.DayDivider -> daySeparatorFactory.create(inner)
is VirtualTimelineItem.ReadMarker -> TimelineItemReadMarkerModel is io.element.android.libraries.matrix.api.timeline.item.virtual.TimelineItemVirtual.VirtualTimelineItem.ReadMarker -> TimelineItemReadMarkerModel
is VirtualTimelineItem.LoadingIndicator -> TimelineItemLoadingModel is io.element.android.libraries.matrix.api.timeline.item.virtual.TimelineItemVirtual.VirtualTimelineItem.LoadingIndicator -> TimelineItemLoadingModel
is VirtualTimelineItem.TimelineStart -> TimelineItemReadMarkerModel is io.element.android.libraries.matrix.api.timeline.item.virtual.TimelineItemVirtual.VirtualTimelineItem.TimelineStart -> TimelineItemReadMarkerModel
else -> TimelineItemUnknownVirtualModel else -> TimelineItemUnknownVirtualModel
} }
} }

View file

@ -30,7 +30,7 @@ open class TimelineItemImageContentProvider : PreviewParameterProvider<TimelineI
fun aTimelineItemImageContent() = TimelineItemImageContent( fun aTimelineItemImageContent() = TimelineItemImageContent(
body = "a body", body = "a body",
imageMeta = MediaResolver.Meta(source = null, kind = MediaResolver.Kind.Content), imageMeta = MediaResolver.Meta(url = null, kind = MediaResolver.Kind.Content),
blurhash = null, blurhash = null,
aspectRatio = 0.5f, aspectRatio = 0.5f,
) )

View file

@ -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.api.media
data class AudioInfo(
val duration: Long?,
val size: Long?
)

View file

@ -0,0 +1,24 @@
/*
* 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.media
data class FileInfo(
val mimetype: String?,
val size: Long?,
val thumbnailInfo: ThumbnailInfo?,
val thumbnailUrl: String?
)

View file

@ -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.api.media
data class ImageInfo(
val height: Long?,
val width: Long?,
val mimetype: String?,
val size: Long?,
val thumbnailInfo: ThumbnailInfo?,
val thumbnailUrl: String?,
val blurhash: String?
)

View file

@ -16,8 +16,6 @@
package io.element.android.libraries.matrix.api.media package io.element.android.libraries.matrix.api.media
import org.matrix.rustcomponents.sdk.MediaSource
interface MediaResolver { interface MediaResolver {
sealed interface Kind { sealed interface Kind {
@ -29,11 +27,10 @@ interface MediaResolver {
} }
data class Meta( data class Meta(
val source: MediaSource?, val url: String?,
val kind: Kind val kind: Kind
) )
suspend fun resolve(url: String?, kind: Kind): ByteArray? suspend fun resolve(url: String?, kind: Kind): ByteArray?
suspend fun resolve(meta: Meta): ByteArray?
} }

View file

@ -0,0 +1,24 @@
/*
* 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.media
data class ThumbnailInfo(
val height: Long?,
val width: Long?,
val mimetype: String?,
val size: Long?
)

View file

@ -0,0 +1,28 @@
/*
* 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.media
data class VideoInfo(
val duration: Long?,
val height: Long?,
val width: Long?,
val mimetype: String?,
val size: Long?,
val thumbnailInfo: ThumbnailInfo?,
val thumbnailUrl: String?,
val blurhash: String?
)

View file

@ -17,13 +17,13 @@
package io.element.android.libraries.matrix.api.timeline package io.element.android.libraries.matrix.api.timeline
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import org.matrix.rustcomponents.sdk.EventTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
import org.matrix.rustcomponents.sdk.VirtualTimelineItem import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
sealed interface MatrixTimelineItem { sealed interface MatrixTimelineItem {
data class Event(val event: EventTimelineItem) : MatrixTimelineItem { data class Event(val event: EventTimelineItem) : MatrixTimelineItem {
val uniqueId: String = event.uniqueIdentifier() val uniqueId: String = event.uniqueIdentifier
val eventId: EventId? = event.eventId()?.let { EventId(it) } val eventId: EventId? = event.eventId
} }
data class Virtual(val virtual: VirtualTimelineItem) : MatrixTimelineItem data class Virtual(val virtual: VirtualTimelineItem) : MatrixTimelineItem

View file

@ -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.api.timeline.item.event
data class EventReaction(
val key: String,
val count: Long
)

View file

@ -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.api.timeline.item.event
import io.element.android.libraries.matrix.api.core.EventId
sealed interface EventSendState {
object NotSendYet : EventSendState
data class SendingFailed(
val error: String
) : EventSendState
data class Sent(
val eventId: EventId
) : EventSendState
}

View file

@ -0,0 +1,35 @@
/*
* 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.EventId
import io.element.android.libraries.matrix.api.core.UserId
data class EventTimelineItem(
val uniqueIdentifier: String,
val eventId: EventId?,
val isEditable: Boolean,
val isLocal: Boolean,
val isOwn: Boolean,
val isRemote: Boolean,
val localSendState: EventSendState?,
val reactions: List<EventReaction>,
val sender: UserId,
val senderProfile: ProfileTimelineDetails,
val timestamp: Long,
val content: TimelineEventContent
)

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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()
)

View file

@ -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()
)

View file

@ -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
)

View file

@ -14,19 +14,23 @@
* limitations under the License. * 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 interface MediaResolver {
import org.matrix.rustcomponents.sdk.TimelineItem
fun TimelineItem.asMatrixTimelineItem(): MatrixTimelineItem { sealed interface Kind {
val asEvent = asEvent() data class Thumbnail(val width: Int, val height: Int) : Kind {
if (asEvent != null) { constructor(size: Int) : this(size, size)
return MatrixTimelineItem.Event(asEvent) }
object Content : Kind
} }
val asVirtual = asVirtual()
if (asVirtual != null) { data class Meta(
return MatrixTimelineItem.Virtual(asVirtual) val url: String?,
} val kind: Kind
return MatrixTimelineItem.Other )
suspend fun resolve(url: String?, kind: Kind): ByteArray?
} }

View file

@ -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() }

View file

@ -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.MatrixClient
import io.element.android.libraries.matrix.api.media.MediaResolver import io.element.android.libraries.matrix.api.media.MediaResolver
import org.matrix.rustcomponents.sdk.MediaSource
import org.matrix.rustcomponents.sdk.mediaSourceFromUrl import org.matrix.rustcomponents.sdk.mediaSourceFromUrl
internal class RustMediaResolver(private val client: MatrixClient) : MediaResolver { internal class RustMediaResolver(private val client: MatrixClient) : MediaResolver {
override suspend fun resolve(url: String?, kind: MediaResolver.Kind): ByteArray? { override suspend fun resolve(url: String?, kind: MediaResolver.Kind): ByteArray? {
if (url.isNullOrEmpty()) return null if (url.isNullOrEmpty()) return null
val mediaSource = mediaSourceFromUrl(url) return mediaSourceFromUrl(url).use { mediaSource ->
return resolve(MediaResolver.Meta(mediaSource, kind)) resolve(mediaSource, kind)
}
} }
override suspend fun resolve(meta: MediaResolver.Meta): ByteArray? { private suspend fun resolve(mediaSource: MediaSource, kind: MediaResolver.Kind): ByteArray? {
val source = meta.source ?: return null
val kind = meta.kind
return when (kind) { return when (kind) {
is MediaResolver.Kind.Content -> client.loadMediaContentForSource(source) is MediaResolver.Kind.Content -> client.loadMediaContentForSource(mediaSource)
is MediaResolver.Kind.Thumbnail -> client.loadMediaThumbnailForSource( is MediaResolver.Kind.Thumbnail -> client.loadMediaThumbnailForSource(
source, mediaSource,
kind.width.toLong(), kind.width.toLong(),
kind.height.toLong() kind.height.toLong()
) )

View file

@ -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()
)

View file

@ -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
)

View file

@ -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.MatrixTimeline
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem 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.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -25,14 +26,15 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.TimelineChange import org.matrix.rustcomponents.sdk.TimelineChange
import org.matrix.rustcomponents.sdk.TimelineDiff import org.matrix.rustcomponents.sdk.TimelineDiff
import org.matrix.rustcomponents.sdk.TimelineItem
import org.matrix.rustcomponents.sdk.TimelineListener import org.matrix.rustcomponents.sdk.TimelineListener
import org.matrix.rustcomponents.sdk.VirtualTimelineItem
internal class MatrixTimelineDiffProcessor( internal class MatrixTimelineDiffProcessor(
private val paginationState: MutableStateFlow<MatrixTimeline.PaginationState>, private val paginationState: MutableStateFlow<MatrixTimeline.PaginationState>,
private val timelineItems: MutableStateFlow<List<MatrixTimelineItem>>, private val timelineItems: MutableStateFlow<List<MatrixTimelineItem>>,
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
private val diffDispatcher: CoroutineDispatcher, private val diffDispatcher: CoroutineDispatcher,
private val timelineItemFactory: MatrixTimelineItemMapper,
) : TimelineListener { ) : TimelineListener {
override fun onUpdate(update: TimelineDiff) { override fun onUpdate(update: TimelineDiff) {
@ -117,4 +119,9 @@ internal class MatrixTimelineDiffProcessor(
} }
} }
} }
private fun TimelineItem.asMatrixTimelineItem(): MatrixTimelineItem {
return timelineItemFactory.map(this)
}
} }

View file

@ -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
}
}

View file

@ -17,11 +17,15 @@
package io.element.android.libraries.matrix.impl.timeline package io.element.android.libraries.matrix.impl.timeline
import io.element.android.libraries.core.coroutine.CoroutineDispatchers 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.core.EventId
import io.element.android.libraries.matrix.api.room.MatrixRoom 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.MatrixTimeline
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem 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.CoroutineScope
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -52,11 +56,21 @@ class RustMatrixTimeline(
MatrixTimeline.PaginationState(canBackPaginate = true, isBackPaginating = false) MatrixTimeline.PaginationState(canBackPaginate = true, isBackPaginating = false)
) )
private val timelineItemFactory = MatrixTimelineItemMapper(
virtualTimelineItemMapper = VirtualTimelineItemMapper(),
eventTimelineItemMapper = EventTimelineItemMapper(
contentMapper = TimelineEventContentMapper(
eventMessageMapper = EventMessageMapper()
)
)
)
private val innerTimelineListener = MatrixTimelineDiffProcessor( private val innerTimelineListener = MatrixTimelineDiffProcessor(
paginationState = paginationState, paginationState = paginationState,
timelineItems = timelineItems, timelineItems = timelineItems,
coroutineScope = coroutineScope, coroutineScope = coroutineScope,
diffDispatcher = coroutineDispatchers.diffUpdateDispatcher diffDispatcher = coroutineDispatchers.diffUpdateDispatcher,
timelineItemFactory = timelineItemFactory,
) )
private val listenerTokens = TaskHandleBag() private val listenerTokens = TaskHandleBag()
@ -83,7 +97,7 @@ class RustMatrixTimeline(
val result = addListener(innerTimelineListener) val result = addListener(innerTimelineListener)
result result
.onSuccess { timelineItems -> .onSuccess { timelineItems ->
val matrixTimelineItems = timelineItems.map { it.asMatrixTimelineItem() } val matrixTimelineItems = timelineItems.map(timelineItemFactory::map)
withContext(coroutineDispatchers.diffUpdateDispatcher) { withContext(coroutineDispatchers.diffUpdateDispatcher) {
this@RustMatrixTimeline.timelineItems.value = matrixTimelineItems this@RustMatrixTimeline.timelineItems.value = matrixTimelineItems
} }

View file

@ -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
}
}

View file

@ -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<Reaction>?.map(): List<EventReaction> {
return this?.map {
EventReaction(
key = it.key,
count = it.count.toLong()
)
} ?: emptyList()
}

View file

@ -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
}
}
}
}

View file

@ -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
}
}
}

View file

@ -27,7 +27,10 @@ class TaskHandleBag(private val tokens: MutableSet<TaskHandle> = CopyOnWriteArra
} }
fun dispose() { fun dispose() {
tokens.forEach { it.cancel() } tokens.forEach {
it.cancel()
it.destroy()
}
tokens.clear() tokens.clear()
} }
} }

View file

@ -22,8 +22,4 @@ class FakeMediaResolver : MediaResolver {
override suspend fun resolve(url: String?, kind: MediaResolver.Kind): ByteArray? { override suspend fun resolve(url: String?, kind: MediaResolver.Kind): ByteArray? {
return null return null
} }
override suspend fun resolve(meta: MediaResolver.Meta): ByteArray? {
return null
}
} }

View file

@ -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.designsystem.components.avatar.AvatarData
import io.element.android.libraries.matrix.api.media.MediaResolver import io.element.android.libraries.matrix.api.media.MediaResolver
import org.matrix.rustcomponents.sdk.mediaSourceFromUrl
fun AvatarData.toMetadata(): MediaResolver.Meta { fun AvatarData.toMetadata(): MediaResolver.Meta {
val mediaSource = url?.let { mediaSourceFromUrl(it) } return MediaResolver.Meta(url = url, kind = MediaResolver.Kind.Thumbnail(size.value))
return MediaResolver.Meta(source = mediaSource, kind = MediaResolver.Kind.Thumbnail(size.value))
} }

View file

@ -33,7 +33,7 @@ internal class MediaFetcher(
) : Fetcher { ) : Fetcher {
override suspend fun fetch(): FetchResult? { 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) val byteBuffer = ByteBuffer.wrap(byteArray)
return imageLoader.components.newFetcher(byteBuffer, options, imageLoader)?.first?.fetch() return imageLoader.components.newFetcher(byteBuffer, options, imageLoader)?.first?.fetch()
} }

View file

@ -33,4 +33,4 @@ internal class MediaKeyer : Keyer<MediaResolver.Meta> {
} }
} }
private fun MediaResolver.Meta.toKey() = "${source?.url()}_${kind}" private fun MediaResolver.Meta.toKey() = "${url}_${kind}"