Merge pull request #1709 from vector-im/feature/bma/secureBackupIteration
Secure backup iteration
This commit is contained in:
commit
ba004187f0
31 changed files with 266 additions and 33 deletions
|
|
@ -19,6 +19,7 @@ package io.element.android.features.logout.impl
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||||
|
import io.element.android.libraries.matrix.api.encryption.SteadyStateException
|
||||||
|
|
||||||
open class LogoutStateProvider : PreviewParameterProvider<LogoutState> {
|
open class LogoutStateProvider : PreviewParameterProvider<LogoutState> {
|
||||||
override val values: Sequence<LogoutState>
|
override val values: Sequence<LogoutState>
|
||||||
|
|
@ -30,6 +31,7 @@ open class LogoutStateProvider : PreviewParameterProvider<LogoutState> {
|
||||||
aLogoutState(showConfirmationDialog = true),
|
aLogoutState(showConfirmationDialog = true),
|
||||||
aLogoutState(logoutAction = Async.Loading()),
|
aLogoutState(logoutAction = Async.Loading()),
|
||||||
aLogoutState(logoutAction = Async.Failure(Exception("Failed to logout"))),
|
aLogoutState(logoutAction = Async.Failure(Exception("Failed to logout"))),
|
||||||
|
aLogoutState(backupUploadState = BackupUploadState.SteadyException(SteadyStateException.Connection("No network"))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||||
import io.element.android.libraries.designsystem.theme.progressIndicatorTrackColor
|
import io.element.android.libraries.designsystem.theme.progressIndicatorTrackColor
|
||||||
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||||
|
import io.element.android.libraries.matrix.api.encryption.SteadyStateException
|
||||||
import io.element.android.libraries.theme.ElementTheme
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
|
|
@ -128,7 +129,6 @@ fun LogoutView(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO i18n
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun HeaderContent(
|
private fun HeaderContent(
|
||||||
state: LogoutState,
|
state: LogoutState,
|
||||||
|
|
@ -137,9 +137,11 @@ private fun HeaderContent(
|
||||||
val title = when {
|
val title = when {
|
||||||
state.backupUploadState.isBackingUp() -> stringResource(id = R.string.screen_signout_key_backup_ongoing_title)
|
state.backupUploadState.isBackingUp() -> stringResource(id = R.string.screen_signout_key_backup_ongoing_title)
|
||||||
state.isLastSession -> stringResource(id = R.string.screen_signout_key_backup_disabled_title)
|
state.isLastSession -> stringResource(id = R.string.screen_signout_key_backup_disabled_title)
|
||||||
else -> "Sign out of Element" // TODO
|
else -> stringResource(CommonStrings.action_signout)
|
||||||
}
|
}
|
||||||
val subtitle = when {
|
val subtitle = when {
|
||||||
|
(state.backupUploadState as? BackupUploadState.SteadyException)?.exception is SteadyStateException.Connection ->
|
||||||
|
stringResource(id = R.string.screen_signout_key_backup_offline_subtitle)
|
||||||
state.backupUploadState.isBackingUp() -> stringResource(id = R.string.screen_signout_key_backup_ongoing_subtitle)
|
state.backupUploadState.isBackingUp() -> stringResource(id = R.string.screen_signout_key_backup_ongoing_subtitle)
|
||||||
state.isLastSession -> stringResource(id = R.string.screen_signout_key_backup_disabled_subtitle)
|
state.isLastSession -> stringResource(id = R.string.screen_signout_key_backup_disabled_subtitle)
|
||||||
else -> null
|
else -> null
|
||||||
|
|
@ -151,7 +153,6 @@ private fun HeaderContent(
|
||||||
iconResourceId = CommonDrawables.ic_key,
|
iconResourceId = CommonDrawables.ic_key,
|
||||||
title = title,
|
title = title,
|
||||||
subTitle = subtitle,
|
subTitle = subtitle,
|
||||||
// iconComposable = iconComposable,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,7 +162,9 @@ private fun BackupUploadState.isBackingUp(): Boolean {
|
||||||
BackupUploadState.Waiting,
|
BackupUploadState.Waiting,
|
||||||
is BackupUploadState.Uploading,
|
is BackupUploadState.Uploading,
|
||||||
is BackupUploadState.CheckingIfUploadNeeded -> true
|
is BackupUploadState.CheckingIfUploadNeeded -> true
|
||||||
BackupUploadState.Done -> false
|
is BackupUploadState.SteadyException -> exception is SteadyStateException.Connection
|
||||||
|
BackupUploadState.Done,
|
||||||
|
BackupUploadState.Error -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
|
@ -30,12 +31,17 @@ import im.vector.app.features.analytics.plan.PollEnd
|
||||||
import im.vector.app.features.analytics.plan.PollVote
|
import im.vector.app.features.analytics.plan.PollVote
|
||||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
|
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
|
import io.element.android.features.messages.impl.timeline.session.SessionState
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
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.encryption.BackupState
|
||||||
|
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||||
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.room.MessageEventType
|
import io.element.android.libraries.matrix.api.room.MessageEventType
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin
|
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin
|
||||||
|
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||||
|
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
||||||
import io.element.android.libraries.matrix.ui.room.canSendMessageAsState
|
import io.element.android.libraries.matrix.ui.room.canSendMessageAsState
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
@ -55,6 +61,8 @@ class TimelinePresenter @Inject constructor(
|
||||||
private val dispatchers: CoroutineDispatchers,
|
private val dispatchers: CoroutineDispatchers,
|
||||||
private val appScope: CoroutineScope,
|
private val appScope: CoroutineScope,
|
||||||
private val analyticsService: AnalyticsService,
|
private val analyticsService: AnalyticsService,
|
||||||
|
private val verificationService: SessionVerificationService,
|
||||||
|
private val encryptionService: EncryptionService,
|
||||||
) : Presenter<TimelineState> {
|
) : Presenter<TimelineState> {
|
||||||
|
|
||||||
private val timeline = room.timeline
|
private val timeline = room.timeline
|
||||||
|
|
@ -77,6 +85,18 @@ class TimelinePresenter @Inject constructor(
|
||||||
val prevMostRecentItemId = rememberSaveable { mutableStateOf<String?>(null) }
|
val prevMostRecentItemId = rememberSaveable { mutableStateOf<String?>(null) }
|
||||||
val hasNewItems = remember { mutableStateOf(false) }
|
val hasNewItems = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val sessionVerifiedStatus by verificationService.sessionVerifiedStatus.collectAsState()
|
||||||
|
val keyBackupState by encryptionService.backupStateStateFlow.collectAsState()
|
||||||
|
|
||||||
|
val sessionState by remember {
|
||||||
|
derivedStateOf {
|
||||||
|
SessionState(
|
||||||
|
isSessionVerified = sessionVerifiedStatus == SessionVerifiedStatus.Verified,
|
||||||
|
isKeyBackupEnabled = keyBackupState == BackupState.ENABLED
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun handleEvents(event: TimelineEvents) {
|
fun handleEvents(event: TimelineEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
TimelineEvents.LoadMore -> localScope.paginateBackwards()
|
TimelineEvents.LoadMore -> localScope.paginateBackwards()
|
||||||
|
|
@ -131,6 +151,7 @@ class TimelinePresenter @Inject constructor(
|
||||||
paginationState = paginationState,
|
paginationState = paginationState,
|
||||||
timelineItems = timelineItems,
|
timelineItems = timelineItems,
|
||||||
hasNewItems = hasNewItems.value,
|
hasNewItems = hasNewItems.value,
|
||||||
|
sessionState = sessionState,
|
||||||
eventSink = ::handleEvents
|
eventSink = ::handleEvents
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
|
import io.element.android.features.messages.impl.timeline.session.SessionState
|
||||||
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.timeline.MatrixTimeline
|
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
@ -29,5 +30,6 @@ data class TimelineState(
|
||||||
val userHasPermissionToSendMessage: Boolean,
|
val userHasPermissionToSendMessage: Boolean,
|
||||||
val paginationState: MatrixTimeline.PaginationState,
|
val paginationState: MatrixTimeline.PaginationState,
|
||||||
val hasNewItems: Boolean,
|
val hasNewItems: Boolean,
|
||||||
|
val sessionState: SessionState,
|
||||||
val eventSink: (TimelineEvents) -> Unit
|
val eventSink: (TimelineEvents) -> Unit
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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.aTimelineItemStateEventContent
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.virtual.aTimelineItemDaySeparatorModel
|
import io.element.android.features.messages.impl.timeline.model.virtual.aTimelineItemDaySeparatorModel
|
||||||
|
import io.element.android.features.messages.impl.timeline.session.aSessionState
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
|
|
@ -46,6 +47,10 @@ fun aTimelineState(timelineItems: ImmutableList<TimelineItem> = persistentListOf
|
||||||
highlightedEventId = null,
|
highlightedEventId = null,
|
||||||
userHasPermissionToSendMessage = true,
|
userHasPermissionToSendMessage = true,
|
||||||
hasNewItems = false,
|
hasNewItems = false,
|
||||||
|
sessionState = aSessionState(
|
||||||
|
isSessionVerified = true,
|
||||||
|
isKeyBackupEnabled = true,
|
||||||
|
),
|
||||||
eventSink = {},
|
eventSink = {},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentProvider
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentProvider
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.canBeRepliedTo
|
import io.element.android.features.messages.impl.timeline.model.event.canBeRepliedTo
|
||||||
|
import io.element.android.features.messages.impl.timeline.session.SessionState
|
||||||
import io.element.android.libraries.designsystem.animation.alphaAnimation
|
import io.element.android.libraries.designsystem.animation.alphaAnimation
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
|
|
@ -133,6 +134,7 @@ fun TimelineView(
|
||||||
onReactionLongClick = onReactionLongClicked,
|
onReactionLongClick = onReactionLongClicked,
|
||||||
onMoreReactionsClick = onMoreReactionsClicked,
|
onMoreReactionsClick = onMoreReactionsClicked,
|
||||||
onTimestampClicked = onTimestampClicked,
|
onTimestampClicked = onTimestampClicked,
|
||||||
|
sessionState = state.sessionState,
|
||||||
eventSink = state.eventSink,
|
eventSink = state.eventSink,
|
||||||
onSwipeToReply = onSwipeToReply,
|
onSwipeToReply = onSwipeToReply,
|
||||||
)
|
)
|
||||||
|
|
@ -162,6 +164,7 @@ private fun TimelineItemRow(
|
||||||
timelineItem: TimelineItem,
|
timelineItem: TimelineItem,
|
||||||
highlightedItem: String?,
|
highlightedItem: String?,
|
||||||
userHasPermissionToSendMessage: Boolean,
|
userHasPermissionToSendMessage: Boolean,
|
||||||
|
sessionState: SessionState,
|
||||||
onUserDataClick: (UserId) -> Unit,
|
onUserDataClick: (UserId) -> Unit,
|
||||||
onClick: (TimelineItem.Event) -> Unit,
|
onClick: (TimelineItem.Event) -> Unit,
|
||||||
onLongClick: (TimelineItem.Event) -> Unit,
|
onLongClick: (TimelineItem.Event) -> Unit,
|
||||||
|
|
@ -178,6 +181,7 @@ private fun TimelineItemRow(
|
||||||
is TimelineItem.Virtual -> {
|
is TimelineItem.Virtual -> {
|
||||||
TimelineItemVirtualRow(
|
TimelineItemVirtualRow(
|
||||||
virtual = timelineItem,
|
virtual = timelineItem,
|
||||||
|
sessionState = sessionState,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -234,6 +238,7 @@ private fun TimelineItemRow(
|
||||||
TimelineItemRow(
|
TimelineItemRow(
|
||||||
timelineItem = subGroupEvent,
|
timelineItem = subGroupEvent,
|
||||||
highlightedItem = highlightedItem,
|
highlightedItem = highlightedItem,
|
||||||
|
sessionState = sessionState,
|
||||||
userHasPermissionToSendMessage = false,
|
userHasPermissionToSendMessage = false,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline.components
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import io.element.android.features.messages.impl.timeline.session.SessionState
|
||||||
import io.element.android.features.messages.impl.timeline.components.virtual.TimelineEncryptedHistoryBannerView
|
import io.element.android.features.messages.impl.timeline.components.virtual.TimelineEncryptedHistoryBannerView
|
||||||
import io.element.android.features.messages.impl.timeline.components.virtual.TimelineItemDaySeparatorView
|
import io.element.android.features.messages.impl.timeline.components.virtual.TimelineItemDaySeparatorView
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
|
|
@ -28,12 +29,13 @@ import io.element.android.features.messages.impl.timeline.model.virtual.Timeline
|
||||||
@Composable
|
@Composable
|
||||||
fun TimelineItemVirtualRow(
|
fun TimelineItemVirtualRow(
|
||||||
virtual: TimelineItem.Virtual,
|
virtual: TimelineItem.Virtual,
|
||||||
|
sessionState: SessionState,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
when (virtual.model) {
|
when (virtual.model) {
|
||||||
is TimelineItemDaySeparatorModel -> TimelineItemDaySeparatorView(virtual.model, modifier)
|
is TimelineItemDaySeparatorModel -> TimelineItemDaySeparatorView(virtual.model, modifier)
|
||||||
TimelineItemReadMarkerModel -> return
|
TimelineItemReadMarkerModel -> return
|
||||||
is TimelineItemEncryptedHistoryBannerVirtualModel -> TimelineEncryptedHistoryBannerView(modifier)
|
is TimelineItemEncryptedHistoryBannerVirtualModel -> TimelineEncryptedHistoryBannerView(sessionState, modifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,19 +16,24 @@
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.timeline.components.virtual
|
package io.element.android.features.messages.impl.timeline.components.virtual
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.features.messages.impl.R
|
import io.element.android.features.messages.impl.R
|
||||||
|
import io.element.android.features.messages.impl.timeline.session.SessionState
|
||||||
|
import io.element.android.features.messages.impl.timeline.session.SessionStateProvider
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||||
|
|
@ -36,7 +41,10 @@ import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||||
import io.element.android.libraries.theme.ElementTheme
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TimelineEncryptedHistoryBannerView(modifier: Modifier = Modifier) {
|
fun TimelineEncryptedHistoryBannerView(
|
||||||
|
sessionState: SessionState,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.padding(start = 16.dp, end = 16.dp, top = 24.dp, bottom = 32.dp)
|
.padding(start = 16.dp, end = 16.dp, top = 24.dp, bottom = 32.dp)
|
||||||
|
|
@ -44,25 +52,35 @@ fun TimelineEncryptedHistoryBannerView(modifier: Modifier = Modifier) {
|
||||||
.border(1.dp, ElementTheme.colors.borderInfoSubtle, MaterialTheme.shapes.small)
|
.border(1.dp, ElementTheme.colors.borderInfoSubtle, MaterialTheme.shapes.small)
|
||||||
.background(ElementTheme.colors.bgInfoSubtle)
|
.background(ElementTheme.colors.bgInfoSubtle)
|
||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
|
modifier = Modifier.size(20.dp),
|
||||||
resourceId = CommonDrawables.ic_compound_info,
|
resourceId = CommonDrawables.ic_compound_info,
|
||||||
contentDescription = "Info",
|
contentDescription = "Info",
|
||||||
tint = ElementTheme.colors.iconInfoPrimary
|
tint = ElementTheme.colors.iconInfoPrimary
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.screen_room_encrypted_history_banner),
|
text = stringResource(sessionState.toStringResId()),
|
||||||
style = ElementTheme.typography.fontBodyMdMedium,
|
style = ElementTheme.typography.fontBodyMdMedium,
|
||||||
color = ElementTheme.colors.textInfoPrimary
|
color = ElementTheme.colors.textInfoPrimary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreviewsDayNight
|
@StringRes
|
||||||
@Composable
|
private fun SessionState.toStringResId(): Int {
|
||||||
internal fun TimelineEncryptedHistoryBannerViewPreview() {
|
return when {
|
||||||
ElementPreview {
|
isSessionVerified.not() -> R.string.screen_room_encrypted_history_banner_unverified
|
||||||
TimelineEncryptedHistoryBannerView()
|
isKeyBackupEnabled.not() -> R.string.screen_room_encrypted_history_banner
|
||||||
|
else -> R.string.screen_room_encrypted_history_banner // TODO strings need to be updated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreviewsDayNight
|
||||||
|
@Composable
|
||||||
|
internal fun TimelineEncryptedHistoryBannerViewPreview(
|
||||||
|
@PreviewParameter(SessionStateProvider::class) sessionState: SessionState,
|
||||||
|
) = ElementPreview {
|
||||||
|
TimelineEncryptedHistoryBannerView(sessionState = sessionState)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,6 @@
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.timeline.model.virtual
|
package io.element.android.features.messages.impl.timeline.model.virtual
|
||||||
|
|
||||||
object TimelineItemEncryptedHistoryBannerVirtualModel : TimelineItemVirtualModel {
|
data object TimelineItemEncryptedHistoryBannerVirtualModel : TimelineItemVirtualModel {
|
||||||
override val type: String = "TimelineItemEncryptedHistoryBannerVirtualModel"
|
override val type: String = "TimelineItemEncryptedHistoryBannerVirtualModel"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,6 @@
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.timeline.model.virtual
|
package io.element.android.features.messages.impl.timeline.model.virtual
|
||||||
|
|
||||||
object TimelineItemReadMarkerModel : TimelineItemVirtualModel {
|
data object TimelineItemReadMarkerModel : TimelineItemVirtualModel {
|
||||||
override val type: String = "TimelineItemReadMarkerModel"
|
override val type: String = "TimelineItemReadMarkerModel"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.features.messages.impl.timeline.session
|
||||||
|
|
||||||
|
data class SessionState(
|
||||||
|
val isSessionVerified: Boolean,
|
||||||
|
val isKeyBackupEnabled: Boolean,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.messages.impl.timeline.session
|
||||||
|
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
|
||||||
|
open class SessionStateProvider : PreviewParameterProvider<SessionState> {
|
||||||
|
override val values: Sequence<SessionState>
|
||||||
|
get() = sequenceOf(
|
||||||
|
aSessionState(isSessionVerified = false, isKeyBackupEnabled = false),
|
||||||
|
aSessionState(isSessionVerified = true, isKeyBackupEnabled = false),
|
||||||
|
aSessionState(isSessionVerified = true, isKeyBackupEnabled = true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun aSessionState(
|
||||||
|
isSessionVerified: Boolean = false,
|
||||||
|
isKeyBackupEnabled: Boolean = false,
|
||||||
|
) = SessionState(
|
||||||
|
isSessionVerified = isSessionVerified,
|
||||||
|
isKeyBackupEnabled = isKeyBackupEnabled,
|
||||||
|
)
|
||||||
|
|
@ -43,7 +43,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPlayer
|
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPlayer
|
||||||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPresenter
|
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPresenter
|
||||||
import io.element.android.features.messages.media.FakeLocalMediaFactory
|
import io.element.android.features.messages.media.FakeLocalMediaFactory
|
||||||
import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer
|
|
||||||
import io.element.android.features.messages.textcomposer.TestRichTextEditorStateFactory
|
import io.element.android.features.messages.textcomposer.TestRichTextEditorStateFactory
|
||||||
import io.element.android.features.messages.timeline.components.customreaction.FakeEmojibaseProvider
|
import io.element.android.features.messages.timeline.components.customreaction.FakeEmojibaseProvider
|
||||||
import io.element.android.features.messages.utils.messagesummary.FakeMessageSummaryFormatter
|
import io.element.android.features.messages.utils.messagesummary.FakeMessageSummaryFormatter
|
||||||
|
|
@ -71,10 +70,13 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||||
import io.element.android.libraries.matrix.test.A_SESSION_ID_2
|
import io.element.android.libraries.matrix.test.A_SESSION_ID_2
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||||
|
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||||
|
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
|
||||||
import io.element.android.libraries.mediapickers.test.FakePickerProvider
|
import io.element.android.libraries.mediapickers.test.FakePickerProvider
|
||||||
|
import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer
|
||||||
import io.element.android.libraries.mediaupload.api.MediaSender
|
import io.element.android.libraries.mediaupload.api.MediaSender
|
||||||
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
||||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||||
|
|
@ -647,6 +649,8 @@ class MessagesPresenterTest {
|
||||||
dispatchers = coroutineDispatchers,
|
dispatchers = coroutineDispatchers,
|
||||||
appScope = this,
|
appScope = this,
|
||||||
analyticsService = analyticsService,
|
analyticsService = analyticsService,
|
||||||
|
encryptionService = FakeEncryptionService(),
|
||||||
|
verificationService = FakeSessionVerificationService(),
|
||||||
)
|
)
|
||||||
val preferencesStore = InMemoryPreferencesStore(isRichTextEditorEnabled = true)
|
val preferencesStore = InMemoryPreferencesStore(isRichTextEditorEnabled = true)
|
||||||
val actionListPresenter = ActionListPresenter(preferencesStore = preferencesStore)
|
val actionListPresenter = ActionListPresenter(preferencesStore = preferencesStore)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import im.vector.app.features.analytics.plan.PollEnd
|
||||||
import im.vector.app.features.analytics.plan.PollVote
|
import im.vector.app.features.analytics.plan.PollVote
|
||||||
import io.element.android.features.messages.fixtures.aMessageEvent
|
import io.element.android.features.messages.fixtures.aMessageEvent
|
||||||
import io.element.android.features.messages.fixtures.aTimelineItemsFactory
|
import io.element.android.features.messages.fixtures.aTimelineItemsFactory
|
||||||
|
import io.element.android.features.messages.impl.timeline.session.SessionState
|
||||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||||
import io.element.android.features.messages.impl.timeline.TimelinePresenter
|
import io.element.android.features.messages.impl.timeline.TimelinePresenter
|
||||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
|
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
|
||||||
|
|
@ -35,10 +36,12 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.ReactionSender
|
import io.element.android.libraries.matrix.api.timeline.item.event.ReactionSender
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
|
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
|
||||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||||
|
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.room.aMessageContent
|
import io.element.android.libraries.matrix.test.room.aMessageContent
|
||||||
import io.element.android.libraries.matrix.test.room.anEventTimelineItem
|
import io.element.android.libraries.matrix.test.room.anEventTimelineItem
|
||||||
import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline
|
import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline
|
||||||
|
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
|
||||||
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
|
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
|
||||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
|
|
@ -67,6 +70,7 @@ class TimelinePresenterTest {
|
||||||
assertThat(initialState.timelineItems).isEmpty()
|
assertThat(initialState.timelineItems).isEmpty()
|
||||||
val loadedNoTimelineState = awaitItem()
|
val loadedNoTimelineState = awaitItem()
|
||||||
assertThat(loadedNoTimelineState.timelineItems).isEmpty()
|
assertThat(loadedNoTimelineState.timelineItems).isEmpty()
|
||||||
|
assertThat(loadedNoTimelineState.sessionState).isEqualTo(SessionState(isSessionVerified = false, isKeyBackupEnabled = false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,8 +232,8 @@ class TimelinePresenterTest {
|
||||||
senders = listOf(alice, charlie)
|
senders = listOf(alice, charlie)
|
||||||
),
|
),
|
||||||
EventReaction(
|
EventReaction(
|
||||||
key = "👍",
|
key = "👍",
|
||||||
senders = listOf(alice, bob)
|
senders = listOf(alice, bob)
|
||||||
),
|
),
|
||||||
EventReaction(
|
EventReaction(
|
||||||
key = "🐶",
|
key = "🐶",
|
||||||
|
|
@ -316,6 +320,8 @@ class TimelinePresenterTest {
|
||||||
dispatchers = testCoroutineDispatchers(),
|
dispatchers = testCoroutineDispatchers(),
|
||||||
appScope = this,
|
appScope = this,
|
||||||
analyticsService = FakeAnalyticsService(),
|
analyticsService = FakeAnalyticsService(),
|
||||||
|
encryptionService = FakeEncryptionService(),
|
||||||
|
verificationService = FakeSessionVerificationService(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -329,6 +335,8 @@ class TimelinePresenterTest {
|
||||||
dispatchers = testCoroutineDispatchers(),
|
dispatchers = testCoroutineDispatchers(),
|
||||||
appScope = this,
|
appScope = this,
|
||||||
analyticsService = analyticsService,
|
analyticsService = analyticsService,
|
||||||
|
encryptionService = FakeEncryptionService(),
|
||||||
|
verificationService = FakeSessionVerificationService(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
|
||||||
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
||||||
molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" }
|
molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" }
|
||||||
timber = "com.jakewharton.timber:timber:5.0.1"
|
timber = "com.jakewharton.timber:timber:5.0.1"
|
||||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.65"
|
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.66"
|
||||||
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
|
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
|
||||||
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
|
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
|
||||||
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
|
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
|
||||||
|
|
|
||||||
|
|
@ -32,4 +32,8 @@ sealed interface BackupUploadState {
|
||||||
) : BackupUploadState
|
) : BackupUploadState
|
||||||
|
|
||||||
data object Done : BackupUploadState
|
data object Done : BackupUploadState
|
||||||
|
|
||||||
|
data object Error : BackupUploadState
|
||||||
|
|
||||||
|
data class SteadyException(val exception: SteadyStateException) : BackupUploadState
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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.encryption
|
||||||
|
|
||||||
|
sealed interface SteadyStateException {
|
||||||
|
/**
|
||||||
|
* The backup can be deleted.
|
||||||
|
*/
|
||||||
|
data class BackupDisabled(val message: String) : SteadyStateException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The task waiting for notifications coming from the upload task can fall behind so much that it lost some notifications.
|
||||||
|
*/
|
||||||
|
data class Lagged(val message: String) : SteadyStateException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The request(s) to upload the room keys failed.
|
||||||
|
*/
|
||||||
|
data class Connection(val message: String) : SteadyStateException
|
||||||
|
}
|
||||||
|
|
@ -36,6 +36,8 @@ class BackupUploadStateMapper {
|
||||||
)
|
)
|
||||||
RustBackupUploadState.Waiting ->
|
RustBackupUploadState.Waiting ->
|
||||||
BackupUploadState.Waiting
|
BackupUploadState.Waiting
|
||||||
|
RustBackupUploadState.Error ->
|
||||||
|
BackupUploadState.Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import org.matrix.rustcomponents.sdk.BackupState as RustBackupState
|
||||||
import org.matrix.rustcomponents.sdk.BackupUploadState as RustBackupUploadState
|
import org.matrix.rustcomponents.sdk.BackupUploadState as RustBackupUploadState
|
||||||
import org.matrix.rustcomponents.sdk.EnableRecoveryProgress as RustEnableRecoveryProgress
|
import org.matrix.rustcomponents.sdk.EnableRecoveryProgress as RustEnableRecoveryProgress
|
||||||
import org.matrix.rustcomponents.sdk.RecoveryState as RustRecoveryState
|
import org.matrix.rustcomponents.sdk.RecoveryState as RustRecoveryState
|
||||||
|
import org.matrix.rustcomponents.sdk.SteadyStateException as RustSteadyStateException
|
||||||
|
|
||||||
internal class RustEncryptionService(
|
internal class RustEncryptionService(
|
||||||
client: Client,
|
client: Client,
|
||||||
|
|
@ -49,6 +50,7 @@ internal class RustEncryptionService(
|
||||||
private val recoveryStateMapper = RecoveryStateMapper()
|
private val recoveryStateMapper = RecoveryStateMapper()
|
||||||
private val enableRecoveryProgressMapper = EnableRecoveryProgressMapper()
|
private val enableRecoveryProgressMapper = EnableRecoveryProgressMapper()
|
||||||
private val backupUploadStateMapper = BackupUploadStateMapper()
|
private val backupUploadStateMapper = BackupUploadStateMapper()
|
||||||
|
private val steadyStateExceptionMapper = SteadyStateExceptionMapper()
|
||||||
|
|
||||||
override val backupStateStateFlow: MutableStateFlow<BackupState> = MutableStateFlow(service.backupState().let(backupStateMapper::map))
|
override val backupStateStateFlow: MutableStateFlow<BackupState> = MutableStateFlow(service.backupState().let(backupStateMapper::map))
|
||||||
override val recoveryStateStateFlow: MutableStateFlow<RecoveryState> = MutableStateFlow(service.recoveryState().let(recoveryStateMapper::map))
|
override val recoveryStateStateFlow: MutableStateFlow<RecoveryState> = MutableStateFlow(service.recoveryState().let(recoveryStateMapper::map))
|
||||||
|
|
@ -98,16 +100,25 @@ internal class RustEncryptionService(
|
||||||
|
|
||||||
override fun waitForBackupUploadSteadyState(): Flow<BackupUploadState> {
|
override fun waitForBackupUploadSteadyState(): Flow<BackupUploadState> {
|
||||||
return callbackFlow {
|
return callbackFlow {
|
||||||
service.waitForBackupUploadSteadyState(
|
runCatching {
|
||||||
progressListener = object : BackupSteadyStateListener {
|
service.waitForBackupUploadSteadyState(
|
||||||
override fun onUpdate(status: RustBackupUploadState) {
|
progressListener = object : BackupSteadyStateListener {
|
||||||
trySend(backupUploadStateMapper.map(status))
|
override fun onUpdate(status: RustBackupUploadState) {
|
||||||
if (status == RustBackupUploadState.Done) {
|
trySend(backupUploadStateMapper.map(status))
|
||||||
close()
|
if (status == RustBackupUploadState.Done) {
|
||||||
|
close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
}.onFailure {
|
||||||
|
if (it is RustSteadyStateException) {
|
||||||
|
trySend(BackupUploadState.SteadyException(steadyStateExceptionMapper.map(it)))
|
||||||
|
} else {
|
||||||
|
trySend(BackupUploadState.Error)
|
||||||
}
|
}
|
||||||
)
|
close()
|
||||||
|
}
|
||||||
awaitClose {}
|
awaitClose {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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.encryption
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.encryption.SteadyStateException
|
||||||
|
import org.matrix.rustcomponents.sdk.SteadyStateException as RustSteadyStateException
|
||||||
|
|
||||||
|
class SteadyStateExceptionMapper {
|
||||||
|
fun map(data: RustSteadyStateException): SteadyStateException {
|
||||||
|
return when (data) {
|
||||||
|
is RustSteadyStateException.BackupDisabled -> SteadyStateException.BackupDisabled(
|
||||||
|
message = data.message
|
||||||
|
)
|
||||||
|
is RustSteadyStateException.Connection -> SteadyStateException.Connection(
|
||||||
|
message = data.message
|
||||||
|
)
|
||||||
|
is RustSteadyStateException.Laged -> SteadyStateException.Lagged(
|
||||||
|
message = data.message
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -528,7 +528,7 @@ class RustMatrixRoom(
|
||||||
override fun acquireCapabilities(capabilities: WidgetCapabilities): WidgetCapabilities {
|
override fun acquireCapabilities(capabilities: WidgetCapabilities): WidgetCapabilities {
|
||||||
return capabilities
|
return capabilities
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:5527d88e8a960a4c644f1a92c59c3f7c65b13decefcbd6b2679db3d64facf010
|
||||||
|
size 39111
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:11ac5615f7abcae9c374b61a0c7d4ecf617ab101285e02eac00d57e200259111
|
||||||
|
size 37104
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:38ae3e66f693072c63a13df6b016d9c29d8e15eb966d0dd43d0443a4f9e37839
|
|
||||||
size 13396
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:482aa8ffda0649841f0247e706aeb133b9786fcfba94f66696238f261c26c27f
|
||||||
|
size 21050
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:9ea317ebaa7f046ac2dd379287d05e8c47eff55fde7d3416de2c68d3a198a1f3
|
||||||
|
size 13361
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:9ea317ebaa7f046ac2dd379287d05e8c47eff55fde7d3416de2c68d3a198a1f3
|
||||||
|
size 13361
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:ce47e90d5cc8d336bc965ae879ab69fc1482254fbd3be524df8eb3c7b40b98df
|
|
||||||
size 13026
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:49ed5149e46df6b7c7691f8c4d6ab9b03b9774d2ce1d04a46a793e7edb11eed0
|
||||||
|
size 20162
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:801ae8d88413708979b1aa9f9f19a3cedd4b81640da4bab7e2c8ed492657d929
|
||||||
|
size 12847
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:801ae8d88413708979b1aa9f9f19a3cedd4b81640da4bab7e2c8ed492657d929
|
||||||
|
size 12847
|
||||||
Loading…
Add table
Add a link
Reference in a new issue