[MatrixSDK] finish mapping timeline and makes it compile

This commit is contained in:
ganfra 2023-03-13 20:18:16 +01:00
parent fb85f35525
commit 801eecfe8d
44 changed files with 370 additions and 242 deletions

View file

@ -80,9 +80,9 @@ import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext
import io.element.android.libraries.matrix.api.auth.AuthenticationException
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
import org.matrix.rustcomponents.sdk.AuthenticationException
import io.element.android.libraries.ui.strings.R as StringR
@OptIn(ExperimentalMaterial3Api::class, ExperimentalTextApi::class)

View file

@ -17,8 +17,8 @@
package io.element.android.features.login.impl.error
import io.element.android.libraries.matrix.api.auth.AuthErrorCode
import io.element.android.libraries.matrix.api.auth.AuthenticationException
import io.element.android.libraries.matrix.api.auth.errorCode
import org.matrix.rustcomponents.sdk.AuthenticationException
import io.element.android.libraries.ui.strings.R.string as StringR
fun loginError(

View file

@ -25,7 +25,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import io.element.android.features.login.impl.util.LoginConstants
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
import kotlinx.coroutines.CoroutineScope
@ -71,15 +70,14 @@ class LoginRootPresenter @Inject constructor(private val authenticationService:
private fun CoroutineScope.submit(homeserver: String, formState: LoginFormState, loggedInState: MutableState<LoggedInState>) = launch {
loggedInState.value = LoggedInState.LoggingIn
//TODO rework the setHomeserver flow
tryOrNull {
authenticationService.setHomeserver(homeserver)
}
try {
val sessionId = authenticationService.login(formState.login.trim(), formState.password.trim())
loggedInState.value = LoggedInState.LoggedIn(sessionId)
} catch (failure: Throwable) {
loggedInState.value = LoggedInState.ErrorLoggingIn(failure)
}
authenticationService.setHomeserver(homeserver)
authenticationService.login(formState.login.trim(), formState.password.trim())
.onSuccess { sessionId ->
loggedInState.value = LoggedInState.LoggedIn(sessionId)
}
.onFailure { failure ->
loggedInState.value = LoggedInState.ErrorLoggingIn(failure)
}
}
private fun updateFormState(formState: MutableState<LoginFormState>, updateLambda: LoginFormState.() -> LoginFormState) {

View file

@ -24,6 +24,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
import io.element.android.libraries.designsystem.components.avatar.AvatarData
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.MatrixTimeline
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@ -81,7 +82,7 @@ internal fun aTimelineItemEvent(
return TimelineItem.Event(
id = randomId,
eventId = EventId(randomId),
senderId = "@senderId",
senderId = UserId("@senderId"),
senderAvatar = AvatarData("@senderId", "sender"),
content = content,
reactionsState = TimelineItemReactions(

View file

@ -24,7 +24,7 @@ import androidx.compose.ui.tooling.preview.Preview
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import org.matrix.rustcomponents.sdk.EncryptedMessage
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
@Composable
fun TimelineItemEncryptedView(
@ -53,7 +53,7 @@ internal fun TimelineItemEncryptedViewDarkPreview() =
private fun ContentToPreview() {
TimelineItemEncryptedView(
content = TimelineItemEncryptedContent(
encryptedMessage = EncryptedMessage.Unknown,
data = UnableToDecryptContent.Data.Unknown
)
)
}

View file

@ -17,11 +17,20 @@
package io.element.android.features.messages.impl.timeline.factories.event
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import org.matrix.rustcomponents.sdk.TimelineItemContentKind
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
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.UnableToDecryptContent
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
import javax.inject.Inject
typealias RustTimelineItemContent = org.matrix.rustcomponents.sdk.TimelineItemContent
class TimelineItemContentFactory @Inject constructor(
private val messageFactory: TimelineItemContentMessageFactory,
private val redactedMessageFactory: TimelineItemContentRedactedFactory,
@ -34,17 +43,18 @@ class TimelineItemContentFactory @Inject constructor(
private val failedToParseStateFactory: TimelineItemContentFailedToParseStateFactory
) {
fun create(itemContent: RustTimelineItemContent): TimelineItemEventContent {
return when (val kind = itemContent.kind()) {
is TimelineItemContentKind.Message -> messageFactory.create(itemContent.asMessage())
is TimelineItemContentKind.RedactedMessage -> redactedMessageFactory.create(kind)
is TimelineItemContentKind.Sticker -> stickerFactory.create(kind)
is TimelineItemContentKind.UnableToDecrypt -> utdFactory.create(kind)
is TimelineItemContentKind.RoomMembership -> roomMembershipFactory.create(kind)
is TimelineItemContentKind.ProfileChange -> profileChangeFactory.create(kind)
is TimelineItemContentKind.State -> stateFactory.create(kind)
is TimelineItemContentKind.FailedToParseMessageLike -> failedToParseMessageFactory.create(kind)
is TimelineItemContentKind.FailedToParseState -> failedToParseStateFactory.create(kind)
fun create(itemContent: TimelineEventContent): TimelineItemEventContent {
return when (itemContent) {
is FailedToParseMessageLikeContent -> failedToParseMessageFactory.create(itemContent)
is FailedToParseStateContent -> failedToParseStateFactory.create(itemContent)
is MessageContent -> messageFactory.create(itemContent)
is ProfileChangeContent -> profileChangeFactory.create(itemContent)
is RedactedContent -> redactedMessageFactory.create(itemContent)
is RoomMembershipContent -> roomMembershipFactory.create(itemContent)
is StateContent -> stateFactory.create(itemContent)
is StickerContent -> stickerFactory.create(itemContent)
is UnableToDecryptContent -> utdFactory.create(itemContent)
is UnknownContent -> TimelineItemUnknownContent
}
}
}

View file

@ -18,12 +18,12 @@ package io.element.android.features.messages.impl.timeline.factories.event
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import org.matrix.rustcomponents.sdk.TimelineItemContentKind
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent
import javax.inject.Inject
class TimelineItemContentFailedToParseMessageFactory @Inject constructor() {
fun create(kind: TimelineItemContentKind.FailedToParseMessageLike): TimelineItemEventContent {
fun create(failedToParseMessageLike: FailedToParseMessageLikeContent): TimelineItemEventContent {
return TimelineItemUnknownContent
}
}

View file

@ -18,12 +18,12 @@ package io.element.android.features.messages.impl.timeline.factories.event
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import org.matrix.rustcomponents.sdk.TimelineItemContentKind
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
import javax.inject.Inject
class TimelineItemContentFailedToParseStateFactory @Inject constructor() {
fun create(kind: TimelineItemContentKind.FailedToParseState): TimelineItemEventContent {
fun create(failedToParseState: FailedToParseStateContent): TimelineItemEventContent {
return TimelineItemUnknownContent
}
}

View file

@ -24,43 +24,46 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import io.element.android.features.messages.impl.timeline.util.toHtmlDocument
import io.element.android.libraries.matrix.api.media.MediaResolver
import org.matrix.rustcomponents.sdk.Message
import org.matrix.rustcomponents.sdk.MessageType
import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
import javax.inject.Inject
class TimelineItemContentMessageFactory @Inject constructor() {
fun create(contentAsMessage: Message?): TimelineItemEventContent {
return when (val messageType = contentAsMessage?.msgtype()) {
is MessageType.Emote -> TimelineItemEmoteContent(
body = messageType.content.body,
htmlDocument = messageType.content.formatted?.toHtmlDocument()
fun create(content: MessageContent): TimelineItemEventContent {
return when (val messageType = content.type) {
is EmoteMessageType -> TimelineItemEmoteContent(
body = messageType.body,
htmlDocument = messageType.formatted?.toHtmlDocument()
)
is MessageType.Image -> {
val height = messageType.content.info?.height?.toFloat()
val width = messageType.content.info?.width?.toFloat()
is ImageMessageType -> {
val height = messageType.info?.height?.toFloat()
val width = messageType.info?.width?.toFloat()
val aspectRatio = if (height != null && width != null) {
width / height
} else {
0.7f
}
TimelineItemImageContent(
body = messageType.content.body,
body = messageType.body,
imageMeta = MediaResolver.Meta(
url = messageType.content.source,
url = messageType.url,
kind = MediaResolver.Kind.Content
),
blurhash = messageType.content.info?.blurhash,
blurhash = messageType.info?.blurhash,
aspectRatio = aspectRatio
)
}
is MessageType.Notice -> TimelineItemNoticeContent(
body = messageType.content.body,
htmlDocument = messageType.content.formatted?.toHtmlDocument()
is NoticeMessageType -> TimelineItemNoticeContent(
body = messageType.body,
htmlDocument = messageType.formatted?.toHtmlDocument()
)
is MessageType.Text -> TimelineItemTextContent(
body = messageType.content.body,
htmlDocument = messageType.content.formatted?.toHtmlDocument()
is TextMessageType -> TimelineItemTextContent(
body = messageType.body,
htmlDocument = messageType.formatted?.toHtmlDocument()
)
else -> TimelineItemUnknownContent
}

View file

@ -18,12 +18,12 @@ package io.element.android.features.messages.impl.timeline.factories.event
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import org.matrix.rustcomponents.sdk.TimelineItemContentKind
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
import javax.inject.Inject
class TimelineItemContentProfileChangeFactory @Inject constructor() {
fun create(kind: TimelineItemContentKind.ProfileChange): TimelineItemEventContent {
fun create(content: ProfileChangeContent): TimelineItemEventContent {
return TimelineItemUnknownContent
}
}

View file

@ -18,12 +18,12 @@ package io.element.android.features.messages.impl.timeline.factories.event
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
import org.matrix.rustcomponents.sdk.TimelineItemContentKind
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
import javax.inject.Inject
class TimelineItemContentRedactedFactory @Inject constructor() {
fun create(kind: TimelineItemContentKind.RedactedMessage): TimelineItemEventContent {
fun create(content: RedactedContent): TimelineItemEventContent {
return TimelineItemRedactedContent
}
}

View file

@ -18,12 +18,12 @@ package io.element.android.features.messages.impl.timeline.factories.event
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import org.matrix.rustcomponents.sdk.TimelineItemContentKind
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
import javax.inject.Inject
class TimelineItemContentRoomMembershipFactory @Inject constructor() {
fun create(kind: TimelineItemContentKind.RoomMembership): TimelineItemEventContent {
fun create(content: RoomMembershipContent): TimelineItemEventContent {
return TimelineItemUnknownContent
}
}

View file

@ -18,12 +18,12 @@ package io.element.android.features.messages.impl.timeline.factories.event
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import org.matrix.rustcomponents.sdk.TimelineItemContentKind
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
import javax.inject.Inject
class TimelineItemContentStateFactory @Inject constructor() {
fun create(kind: TimelineItemContentKind.State): TimelineItemEventContent {
fun create(content: StateContent): TimelineItemEventContent {
return TimelineItemUnknownContent
}
}

View file

@ -18,12 +18,12 @@ package io.element.android.features.messages.impl.timeline.factories.event
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import org.matrix.rustcomponents.sdk.TimelineItemContentKind
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
import javax.inject.Inject
class TimelineItemContentStickerFactory @Inject constructor() {
fun create(kind: TimelineItemContentKind.Sticker): TimelineItemEventContent {
fun create(content: StickerContent): TimelineItemEventContent {
return TimelineItemUnknownContent
}
}

View file

@ -16,14 +16,14 @@
package io.element.android.features.messages.impl.timeline.factories.event
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent
import org.matrix.rustcomponents.sdk.TimelineItemContentKind
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
import javax.inject.Inject
class TimelineItemContentUTDFactory @Inject constructor() {
fun create(kind: TimelineItemContentKind.UnableToDecrypt): TimelineItemEventContent {
return TimelineItemEncryptedContent(kind.msg)
fun create(content: UnableToDecryptContent): TimelineItemEventContent {
return TimelineItemEncryptedContent(content.data)
}
}

View file

@ -23,8 +23,8 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItemReac
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
import kotlinx.collections.immutable.toImmutableList
import org.matrix.rustcomponents.sdk.ProfileTimelineDetails
import javax.inject.Inject
class TimelineItemEventFactory @Inject constructor(
@ -36,13 +36,13 @@ class TimelineItemEventFactory @Inject constructor(
index: Int,
timelineItems: List<MatrixTimelineItem>,
): TimelineItem.Event {
val currentSender = currentTimelineItem.event.sender()
val currentSender = currentTimelineItem.event.sender
val groupPosition =
computeGroupPosition(currentTimelineItem, timelineItems, index)
val senderDisplayName: String?
val senderAvatarUrl: String?
when (val senderProfile = currentTimelineItem.event.senderProfile()) {
when (val senderProfile = currentTimelineItem.event.senderProfile) {
ProfileTimelineDetails.Unavailable,
ProfileTimelineDetails.Pending,
is ProfileTimelineDetails.Error -> {
@ -56,8 +56,8 @@ class TimelineItemEventFactory @Inject constructor(
}
val senderAvatarData = AvatarData(
id = currentSender,
name = senderDisplayName ?: currentSender,
id = currentSender.value,
name = senderDisplayName ?: currentSender.value,
url = senderAvatarUrl,
size = AvatarSize.SMALL
)
@ -67,15 +67,15 @@ class TimelineItemEventFactory @Inject constructor(
senderId = currentSender,
senderDisplayName = senderDisplayName,
senderAvatar = senderAvatarData,
content = contentFactory.create(currentTimelineItem.event.content()),
isMine = currentTimelineItem.event.isOwn(),
content = contentFactory.create(currentTimelineItem.event.content),
isMine = currentTimelineItem.event.isOwn,
groupPosition = groupPosition,
reactionsState = currentTimelineItem.computeReactionsState()
)
}
private fun MatrixTimelineItem.Event.computeReactionsState(): TimelineItemReactions {
val aggregatedReactions = event.reactions().orEmpty().map {
val aggregatedReactions = event.reactions.map {
AggregatedReaction(key = it.key, count = it.count.toString(), isHighlighted = false)
}
return TimelineItemReactions(aggregatedReactions.toImmutableList())
@ -90,9 +90,9 @@ class TimelineItemEventFactory @Inject constructor(
timelineItems.getOrNull(index - 1) as? MatrixTimelineItem.Event
val nextTimelineItem =
timelineItems.getOrNull(index + 1) as? MatrixTimelineItem.Event
val currentSender = currentTimelineItem.event.sender()
val previousSender = prevTimelineItem?.event?.sender()
val nextSender = nextTimelineItem?.event?.sender()
val currentSender = currentTimelineItem.event.sender
val previousSender = prevTimelineItem?.event?.sender
val nextSender = nextTimelineItem?.event?.sender
return when {
previousSender != currentSender && nextSender == currentSender -> TimelineItemGroupPosition.First

View file

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

View file

@ -21,11 +21,12 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemVirtualModel
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId
@Immutable
sealed interface TimelineItem {
fun identifier(): String = when(this){
fun identifier(): String = when (this) {
is Event -> id
is Virtual -> id
}
@ -40,7 +41,7 @@ sealed interface TimelineItem {
data class Event(
val id: String,
val eventId: EventId? = null,
val senderId: String,
val senderId: UserId,
val senderDisplayName: String?,
val senderAvatar: AvatarData,
val content: TimelineItemEventContent,
@ -52,6 +53,6 @@ sealed interface TimelineItem {
val showSenderInformation = groupPosition.isNew() && !isMine
val safeSenderName: String = senderDisplayName ?: senderId
val safeSenderName: String = senderDisplayName ?: senderId.value
}
}

View file

@ -16,8 +16,8 @@
package io.element.android.features.messages.impl.timeline.model.event
import org.matrix.rustcomponents.sdk.EncryptedMessage
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
data class TimelineItemEncryptedContent(
val encryptedMessage: EncryptedMessage
val data: UnableToDecryptContent.Data
) : TimelineItemEventContent

View file

@ -17,8 +17,8 @@
package io.element.android.features.messages.impl.timeline.model.event
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
import org.jsoup.Jsoup
import org.matrix.rustcomponents.sdk.EncryptedMessage
class TimelineItemEventContentProvider : PreviewParameterProvider<TimelineItemEventContent> {
override val values = sequenceOf(
@ -49,7 +49,7 @@ fun aTimelineItemEmoteContent() = TimelineItemEmoteContent(
)
fun aTimelineItemEncryptedContent() = TimelineItemEncryptedContent(
encryptedMessage = EncryptedMessage.Unknown
data = UnableToDecryptContent.Data.Unknown
)
fun aTimelineItemNoticeContent() = TimelineItemNoticeContent(

View file

@ -16,10 +16,10 @@
package io.element.android.features.messages.impl.timeline.util
import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody
import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.matrix.rustcomponents.sdk.FormattedBody
import org.matrix.rustcomponents.sdk.MessageFormat
fun FormattedBody.toHtmlDocument(): Document? {
return takeIf { it.format == MessageFormat.HTML }?.body?.let { formattedBody ->