From 18959a930fbcd96a302b9b2cb5bc25933d2b133c Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 27 Aug 2024 17:45:22 +0200 Subject: [PATCH] Rust sdk : refactor LocalSendState to use the new failure states as iOS does --- .../impl/timeline/TimelineStateProvider.kt | 4 +-- .../components/TimelineEventTimestampView.kt | 6 ++-- ...melineItemEventForTimestampViewProvider.kt | 8 ++--- ...iewStateForTimelineItemEventRowProvider.kt | 2 +- .../receipt/ReadReceiptViewStateProvider.kt | 2 +- .../receipt/TimelineItemReadReceiptView.kt | 33 +++++++++---------- .../impl/timeline/model/TimelineItem.kt | 2 +- .../item/event/LocalEventSendState.kt | 22 ++++++++++--- .../item/event/EventTimelineItemMapper.kt | 16 ++++++--- 9 files changed, 57 insertions(+), 38 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index 677989a81a..f1abc52725 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt @@ -84,7 +84,7 @@ internal fun aTimelineItemList(content: TimelineItemEventContent): ImmutableList isMine = false, content = content, groupPosition = TimelineItemGroupPosition.Middle, - sendState = LocalEventSendState.SendingFailed.Unrecoverable("Message failed to send"), + sendState = LocalEventSendState.Failed.Unknown("Message failed to send"), ), aTimelineItemEvent( isMine = false, @@ -107,7 +107,7 @@ internal fun aTimelineItemList(content: TimelineItemEventContent): ImmutableList isMine = true, content = content, groupPosition = TimelineItemGroupPosition.Middle, - sendState = LocalEventSendState.SendingFailed.Unrecoverable("Message failed to send"), + sendState = LocalEventSendState.Failed.Unknown("Message failed to send"), ), aTimelineItemEvent( isMine = true, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt index 69c2a807a2..86781e5c87 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt @@ -51,10 +51,10 @@ fun TimelineEventTimestampView( modifier: Modifier = Modifier, ) { val formattedTime = event.sentTime - val hasUnrecoverableError = event.localSendState is LocalEventSendState.SendingFailed.Unrecoverable + val hasError = event.localSendState is LocalEventSendState.Failed val hasEncryptionCritical = event.messageShield?.isCritical.orFalse() val isMessageEdited = event.content.isEdited() - val tint = if (hasUnrecoverableError || hasEncryptionCritical) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.secondary + val tint = if (hasError || hasEncryptionCritical) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.secondary Row( modifier = Modifier .padding(PaddingValues(start = TimelineEventTimestampViewDefaults.spacing)) @@ -74,7 +74,7 @@ fun TimelineEventTimestampView( style = ElementTheme.typography.fontBodyXsRegular, color = tint, ) - if (hasUnrecoverableError) { + if (hasError) { Spacer(modifier = Modifier.width(2.dp)) Icon( imageVector = CompoundIcons.Error(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt index 705ac36df5..b7237720bd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt @@ -27,15 +27,13 @@ class TimelineItemEventForTimestampViewProvider : PreviewParameterProvider get() = sequenceOf( aTimelineItemEvent(), - // Sending failed recoverable - aTimelineItemEvent().copy(localSendState = LocalEventSendState.SendingFailed.Recoverable("AN_ERROR")), - // Sending failed unrecoverable - aTimelineItemEvent().copy(localSendState = LocalEventSendState.SendingFailed.Unrecoverable("AN_ERROR")), + aTimelineItemEvent().copy(localSendState = LocalEventSendState.Sending), + aTimelineItemEvent().copy(localSendState = LocalEventSendState.Failed.Unknown("AN_ERROR")), // Edited aTimelineItemEvent().copy(content = aTimelineItemTextContent().copy(isEdited = true)), // Sending failed + Edited (not sure this is possible IRL, but should be covered by test) aTimelineItemEvent().copy( - localSendState = LocalEventSendState.SendingFailed.Unrecoverable("AN_ERROR"), + localSendState = LocalEventSendState.Failed.Unknown("AN_ERROR"), content = aTimelineItemTextContent().copy(isEdited = true), ), aTimelineItemEvent().copy( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateForTimelineItemEventRowProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateForTimelineItemEventRowProvider.kt index 1595e59f54..20bca9fbd7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateForTimelineItemEventRowProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateForTimelineItemEventRowProvider.kt @@ -26,7 +26,7 @@ class ReadReceiptViewStateForTimelineItemEventRowProvider : override val values: Sequence get() = sequenceOf( aReadReceiptViewState( - sendState = LocalEventSendState.NotSentYet + sendState = LocalEventSendState.Sending, ), aReadReceiptViewState( sendState = LocalEventSendState.Sent(EventId("\$eventId")), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt index a40e94c529..6288919abe 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt @@ -29,7 +29,7 @@ class ReadReceiptViewStateProvider : PreviewParameterProvider get() = sequenceOf( aReadReceiptViewState(), - aReadReceiptViewState(sendState = LocalEventSendState.NotSentYet), + aReadReceiptViewState(sendState = LocalEventSendState.Sending), aReadReceiptViewState(sendState = LocalEventSendState.Sent(EventId("\$eventId"))), aReadReceiptViewState( sendState = LocalEventSendState.Sent(EventId("\$eventId")), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt index 0342f684dd..d8cdcbe05f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt @@ -70,19 +70,18 @@ fun TimelineItemReadReceiptView( ReadReceiptsAvatars( receipts = state.receipts, modifier = Modifier - .testTag(TestTags.messageReadReceipts) - .clip(RoundedCornerShape(4.dp)) - .clickable { - onReadReceiptsClick() - } - .padding(2.dp) + .testTag(TestTags.messageReadReceipts) + .clip(RoundedCornerShape(4.dp)) + .clickable { + onReadReceiptsClick() + } + .padding(2.dp) ) } } } else { when (state.sendState) { - LocalEventSendState.NotSentYet, - is LocalEventSendState.SendingFailed.Recoverable -> { + LocalEventSendState.Sending -> { ReadReceiptsRow(modifier) { Icon( modifier = Modifier.padding(2.dp), @@ -92,7 +91,7 @@ fun TimelineItemReadReceiptView( ) } } - is LocalEventSendState.SendingFailed.Unrecoverable -> { + is LocalEventSendState.Failed -> { // Error? The timestamp is already displayed in red } null, @@ -119,9 +118,9 @@ private fun ReadReceiptsRow( ) { Row( modifier = modifier - .fillMaxWidth() - .height(AvatarSize.TimelineReadReceipt.dp + 8.dp) - .padding(horizontal = 18.dp), + .fillMaxWidth() + .height(AvatarSize.TimelineReadReceipt.dp + 8.dp) + .padding(horizontal = 18.dp), horizontalArrangement = Arrangement.End, verticalAlignment = Alignment.CenterVertically, ) { @@ -160,11 +159,11 @@ private fun ReadReceiptsAvatars( .forEachIndexed { index, readReceiptData -> Box( modifier = Modifier - .padding(end = (12.dp + avatarStrokeSize * 2) * index) - .size(size = avatarSize + avatarStrokeSize * 2) - .clip(CircleShape) - .background(avatarStrokeColor) - .zIndex(index.toFloat()), + .padding(end = (12.dp + avatarStrokeSize * 2) * index) + .size(size = avatarSize + avatarStrokeSize * 2) + .clip(CircleShape) + .background(avatarStrokeColor) + .zIndex(index.toFloat()), contentAlignment = Alignment.Center, ) { Avatar( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt index 208a6b07dc..2cb872e95f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt @@ -90,7 +90,7 @@ sealed interface TimelineItem { val safeSenderName: String = senderProfile.getDisambiguatedDisplayName(senderId) - val failedToSend: Boolean = localSendState is LocalEventSendState.SendingFailed + val failedToSend: Boolean = localSendState is LocalEventSendState.Failed val isTextMessage: Boolean = content is TimelineItemTextBasedContent diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/LocalEventSendState.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/LocalEventSendState.kt index b956e51168..8e5f539d81 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/LocalEventSendState.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/LocalEventSendState.kt @@ -18,13 +18,27 @@ package io.element.android.libraries.matrix.api.timeline.item.event import androidx.compose.runtime.Immutable import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.UserId @Immutable sealed interface LocalEventSendState { - data object NotSentYet : LocalEventSendState - sealed class SendingFailed(open val error: String) : LocalEventSendState { - data class Recoverable(override val error: String) : SendingFailed(error) - data class Unrecoverable(override val error: String) : SendingFailed(error) + data object Sending : LocalEventSendState + sealed interface Failed : LocalEventSendState { + data class Unknown(val error: String) : Failed + data class VerifiedUserHasUnsignedDevice( + /** + * The unsigned devices belonging to verified users. A map from user ID + * to a list of device IDs. + */ + val devices: Map> + ) : Failed + + data class VerifiedUserChangedIdentity( + /** + * The users that were previously verified but are no longer. + */ + val users: List + ) : Failed } data class Sent( val eventId: EventId 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 index 8a155b2826..d51e4bf3c8 100644 --- 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 @@ -81,15 +81,23 @@ fun RustProfileDetails.map(): ProfileTimelineDetails { fun RustEventSendState?.map(): LocalEventSendState? { return when (this) { null -> null - RustEventSendState.NotSentYet -> LocalEventSendState.NotSentYet + RustEventSendState.NotSentYet -> LocalEventSendState.Sending is RustEventSendState.SendingFailed -> { - if (this.isRecoverable) { - LocalEventSendState.SendingFailed.Recoverable(this.error) + if (isRecoverable) { + LocalEventSendState.Sending } else { - LocalEventSendState.SendingFailed.Unrecoverable(this.error) + LocalEventSendState.Failed.Unknown(error) } } is RustEventSendState.Sent -> LocalEventSendState.Sent(EventId(eventId)) + is RustEventSendState.VerifiedUserChangedIdentity -> { + LocalEventSendState.Failed.VerifiedUserChangedIdentity(users.map { UserId(it) }) + } + is RustEventSendState.VerifiedUserHasUnsignedDevice -> { + LocalEventSendState.Failed.VerifiedUserHasUnsignedDevice( + devices = devices.mapKeys { UserId(it.key) } + ) + } } }