Browse Source

TimelineEncryptedHistoryBannerView: render depend on verification and key backup state (#1576)

pull/1709/head
Benoit Marty 11 months ago committed by Benoit Marty
parent
commit
4fb825b5cb
  1. 21
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt
  2. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt
  3. 5
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt
  4. 5
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt
  5. 4
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemVirtualRow.kt
  6. 32
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt
  7. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/virtual/TimelineItemEncryptedHistoryBannerVirtualModel.kt
  8. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/virtual/TimelineItemReadMarkerModel.kt
  9. 22
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/session/SessionState.kt
  10. 36
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/session/SessionStateProvider.kt
  11. 6
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt
  12. 12
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt
  13. 2
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt

21
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt

@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable @@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
@ -30,12 +31,17 @@ import im.vector.app.features.analytics.plan.PollEnd @@ -30,12 +31,17 @@ import im.vector.app.features.analytics.plan.PollEnd
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.model.TimelineItem
import io.element.android.features.messages.impl.timeline.session.SessionState
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
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.MessageEventType
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.services.analytics.api.AnalyticsService
import kotlinx.collections.immutable.ImmutableList
@ -55,6 +61,8 @@ class TimelinePresenter @Inject constructor( @@ -55,6 +61,8 @@ class TimelinePresenter @Inject constructor(
private val dispatchers: CoroutineDispatchers,
private val appScope: CoroutineScope,
private val analyticsService: AnalyticsService,
private val verificationService: SessionVerificationService,
private val encryptionService: EncryptionService,
) : Presenter<TimelineState> {
private val timeline = room.timeline
@ -77,6 +85,18 @@ class TimelinePresenter @Inject constructor( @@ -77,6 +85,18 @@ class TimelinePresenter @Inject constructor(
val prevMostRecentItemId = rememberSaveable { mutableStateOf<String?>(null) }
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) {
when (event) {
TimelineEvents.LoadMore -> localScope.paginateBackwards()
@ -131,6 +151,7 @@ class TimelinePresenter @Inject constructor( @@ -131,6 +151,7 @@ class TimelinePresenter @Inject constructor(
paginationState = paginationState,
timelineItems = timelineItems,
hasNewItems = hasNewItems.value,
sessionState = sessionState,
eventSink = ::handleEvents
)
}

2
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt

@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline @@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline
import androidx.compose.runtime.Immutable
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.timeline.MatrixTimeline
import kotlinx.collections.immutable.ImmutableList
@ -29,5 +30,6 @@ data class TimelineState( @@ -29,5 +30,6 @@ data class TimelineState(
val userHasPermissionToSendMessage: Boolean,
val paginationState: MatrixTimeline.PaginationState,
val hasNewItems: Boolean,
val sessionState: SessionState,
val eventSink: (TimelineEvents) -> Unit
)

5
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt

@ -24,6 +24,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt @@ -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.aTimelineItemTextContent
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.AvatarSize
import io.element.android.libraries.matrix.api.core.EventId
@ -46,6 +47,10 @@ fun aTimelineState(timelineItems: ImmutableList<TimelineItem> = persistentListOf @@ -46,6 +47,10 @@ fun aTimelineState(timelineItems: ImmutableList<TimelineItem> = persistentListOf
highlightedEventId = null,
userHasPermissionToSendMessage = true,
hasNewItems = false,
sessionState = aSessionState(
isSessionVerified = true,
isKeyBackupEnabled = true,
),
eventSink = {},
)

5
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt

@ -67,6 +67,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt @@ -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.TimelineItemStateContent
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.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@ -133,6 +134,7 @@ fun TimelineView( @@ -133,6 +134,7 @@ fun TimelineView(
onReactionLongClick = onReactionLongClicked,
onMoreReactionsClick = onMoreReactionsClicked,
onTimestampClicked = onTimestampClicked,
sessionState = state.sessionState,
eventSink = state.eventSink,
onSwipeToReply = onSwipeToReply,
)
@ -162,6 +164,7 @@ private fun TimelineItemRow( @@ -162,6 +164,7 @@ private fun TimelineItemRow(
timelineItem: TimelineItem,
highlightedItem: String?,
userHasPermissionToSendMessage: Boolean,
sessionState: SessionState,
onUserDataClick: (UserId) -> Unit,
onClick: (TimelineItem.Event) -> Unit,
onLongClick: (TimelineItem.Event) -> Unit,
@ -178,6 +181,7 @@ private fun TimelineItemRow( @@ -178,6 +181,7 @@ private fun TimelineItemRow(
is TimelineItem.Virtual -> {
TimelineItemVirtualRow(
virtual = timelineItem,
sessionState = sessionState,
modifier = modifier,
)
}
@ -234,6 +238,7 @@ private fun TimelineItemRow( @@ -234,6 +238,7 @@ private fun TimelineItemRow(
TimelineItemRow(
timelineItem = subGroupEvent,
highlightedItem = highlightedItem,
sessionState = sessionState,
userHasPermissionToSendMessage = false,
onClick = onClick,
onLongClick = onLongClick,

4
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemVirtualRow.kt

@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline.components @@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline.components
import androidx.compose.runtime.Composable
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.TimelineItemDaySeparatorView
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 @@ -28,12 +29,13 @@ import io.element.android.features.messages.impl.timeline.model.virtual.Timeline
@Composable
fun TimelineItemVirtualRow(
virtual: TimelineItem.Virtual,
sessionState: SessionState,
modifier: Modifier = Modifier
) {
when (virtual.model) {
is TimelineItemDaySeparatorModel -> TimelineItemDaySeparatorView(virtual.model, modifier)
TimelineItemReadMarkerModel -> return
is TimelineItemEncryptedHistoryBannerVirtualModel -> TimelineEncryptedHistoryBannerView(modifier)
is TimelineItemEncryptedHistoryBannerVirtualModel -> TimelineEncryptedHistoryBannerView(sessionState, modifier)
}
}

32
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt

@ -16,19 +16,24 @@ @@ -16,19 +16,24 @@
package io.element.android.features.messages.impl.timeline.components.virtual
import androidx.annotation.StringRes
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
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.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Icon
@ -36,7 +41,10 @@ import io.element.android.libraries.designsystem.utils.CommonDrawables @@ -36,7 +41,10 @@ import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
@Composable
fun TimelineEncryptedHistoryBannerView(modifier: Modifier = Modifier) {
fun TimelineEncryptedHistoryBannerView(
sessionState: SessionState,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.padding(start = 16.dp, end = 16.dp, top = 24.dp, bottom = 32.dp)
@ -44,25 +52,35 @@ fun TimelineEncryptedHistoryBannerView(modifier: Modifier = Modifier) { @@ -44,25 +52,35 @@ fun TimelineEncryptedHistoryBannerView(modifier: Modifier = Modifier) {
.border(1.dp, ElementTheme.colors.borderInfoSubtle, MaterialTheme.shapes.small)
.background(ElementTheme.colors.bgInfoSubtle)
.padding(16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp)
horizontalArrangement = Arrangement.spacedBy(16.dp),
) {
Icon(
modifier = Modifier.size(20.dp),
resourceId = CommonDrawables.ic_compound_info,
contentDescription = "Info",
tint = ElementTheme.colors.iconInfoPrimary
)
Text(
text = stringResource(R.string.screen_room_encrypted_history_banner),
text = stringResource(sessionState.toStringResId()),
style = ElementTheme.typography.fontBodyMdMedium,
color = ElementTheme.colors.textInfoPrimary
)
}
}
@StringRes
private fun SessionState.toStringResId(): Int {
return when {
isSessionVerified.not() -> R.string.screen_room_encrypted_history_banner_unverified
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() {
ElementPreview {
TimelineEncryptedHistoryBannerView()
}
internal fun TimelineEncryptedHistoryBannerViewPreview(
@PreviewParameter(SessionStateProvider::class) sessionState: SessionState,
) = ElementPreview {
TimelineEncryptedHistoryBannerView(sessionState = sessionState)
}

2
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/virtual/TimelineItemEncryptedHistoryBannerVirtualModel.kt

@ -16,6 +16,6 @@ @@ -16,6 +16,6 @@
package io.element.android.features.messages.impl.timeline.model.virtual
object TimelineItemEncryptedHistoryBannerVirtualModel : TimelineItemVirtualModel {
data object TimelineItemEncryptedHistoryBannerVirtualModel : TimelineItemVirtualModel {
override val type: String = "TimelineItemEncryptedHistoryBannerVirtualModel"
}

2
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/virtual/TimelineItemReadMarkerModel.kt

@ -16,6 +16,6 @@ @@ -16,6 +16,6 @@
package io.element.android.features.messages.impl.timeline.model.virtual
object TimelineItemReadMarkerModel : TimelineItemVirtualModel {
data object TimelineItemReadMarkerModel : TimelineItemVirtualModel {
override val type: String = "TimelineItemReadMarkerModel"
}

22
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/session/SessionState.kt

@ -0,0 +1,22 @@ @@ -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,
)

36
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/session/SessionStateProvider.kt

@ -0,0 +1,36 @@ @@ -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,
)

6
features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt

@ -43,7 +43,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt @@ -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.VoiceMessageComposerPresenter
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.timeline.components.customreaction.FakeEmojibaseProvider
import io.element.android.features.messages.utils.messagesummary.FakeMessageSummaryFormatter
@ -71,10 +70,13 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID @@ -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.FakeMatrixClient
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.aRoomInfo
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.mediaplayer.test.FakeMediaPlayer
import io.element.android.libraries.mediaupload.api.MediaSender
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
import io.element.android.libraries.permissions.api.PermissionsPresenter
@ -647,6 +649,8 @@ class MessagesPresenterTest { @@ -647,6 +649,8 @@ class MessagesPresenterTest {
dispatchers = coroutineDispatchers,
appScope = this,
analyticsService = analyticsService,
encryptionService = FakeEncryptionService(),
verificationService = FakeSessionVerificationService(),
)
val preferencesStore = InMemoryPreferencesStore(isRichTextEditorEnabled = true)
val actionListPresenter = ActionListPresenter(preferencesStore = preferencesStore)

12
features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt

@ -24,6 +24,7 @@ import im.vector.app.features.analytics.plan.PollEnd @@ -24,6 +24,7 @@ import im.vector.app.features.analytics.plan.PollEnd
import im.vector.app.features.analytics.plan.PollVote
import io.element.android.features.messages.fixtures.aMessageEvent
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.TimelinePresenter
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 @@ -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.virtual.VirtualTimelineItem
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.aMessageContent
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.verification.FakeSessionVerificationService
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.WarmUpRule
@ -67,6 +70,7 @@ class TimelinePresenterTest { @@ -67,6 +70,7 @@ class TimelinePresenterTest {
assertThat(initialState.timelineItems).isEmpty()
val loadedNoTimelineState = awaitItem()
assertThat(loadedNoTimelineState.timelineItems).isEmpty()
assertThat(loadedNoTimelineState.sessionState).isEqualTo(SessionState(isSessionVerified = false, isKeyBackupEnabled = false))
}
}
@ -228,8 +232,8 @@ class TimelinePresenterTest { @@ -228,8 +232,8 @@ class TimelinePresenterTest {
senders = listOf(alice, charlie)
),
EventReaction(
key = "👍",
senders = listOf(alice, bob)
key = "👍",
senders = listOf(alice, bob)
),
EventReaction(
key = "🐶",
@ -316,6 +320,8 @@ class TimelinePresenterTest { @@ -316,6 +320,8 @@ class TimelinePresenterTest {
dispatchers = testCoroutineDispatchers(),
appScope = this,
analyticsService = FakeAnalyticsService(),
encryptionService = FakeEncryptionService(),
verificationService = FakeSessionVerificationService(),
)
}
@ -329,6 +335,8 @@ class TimelinePresenterTest { @@ -329,6 +335,8 @@ class TimelinePresenterTest {
dispatchers = testCoroutineDispatchers(),
appScope = this,
analyticsService = analyticsService,
encryptionService = FakeEncryptionService(),
verificationService = FakeSessionVerificationService(),
)
}
}

2
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt

@ -528,7 +528,7 @@ class RustMatrixRoom( @@ -528,7 +528,7 @@ class RustMatrixRoom(
override fun acquireCapabilities(capabilities: WidgetCapabilities): WidgetCapabilities {
return capabilities
}
},
},
)
}

Loading…
Cancel
Save