From 920c50cc3d0eb9304252d217b4af4acb7a8d8234 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 31 Oct 2024 09:19:08 +0100 Subject: [PATCH 01/50] Compile and target API 35. Tested OK on a API 35 emulator. --- .../element/android/features/call/impl/ui/CallScreenView.kt | 1 + .../android/features/share/impl/DefaultShareService.kt | 2 +- .../element/android/libraries/oidc/impl/webview/OidcView.kt | 1 + plugins/src/main/kotlin/Versions.kt | 4 ++-- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt index ce9319d0cf..711b203f99 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt @@ -183,6 +183,7 @@ private fun WebView.setup( allowFileAccess = true domStorageEnabled = true mediaPlaybackRequiresUserGesture = false + @Suppress("DEPRECATION") databaseEnabled = true loadsImagesAutomatically = true userAgentString = userAgent diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareService.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareService.kt index de53096076..f4ed61dba8 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareService.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareService.kt @@ -46,7 +46,7 @@ class DefaultShareService @Inject constructor( PackageManager.GET_ACTIVITIES or PackageManager.MATCH_DISABLED_COMPONENTS ) .activities - .firstOrNull { it.name.endsWith(".ShareActivity") } + ?.firstOrNull { it.name.endsWith(".ShareActivity") } ?.let { shareActivityInfo -> ComponentName( shareActivityInfo.packageName, diff --git a/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/webview/OidcView.kt b/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/webview/OidcView.kt index 055d516f97..d58eebf8ae 100644 --- a/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/webview/OidcView.kt +++ b/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/webview/OidcView.kt @@ -86,6 +86,7 @@ fun OidcView( javaScriptEnabled = true allowContentAccess = true allowFileAccess = true + @Suppress("DEPRECATION") databaseEnabled = true domStorageEnabled = true } diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index c71a77ac47..61df38ebd7 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -52,8 +52,8 @@ private const val versionPatch = 3 object Versions { val versionCode = 4_000_000 + versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch val versionName = "$versionMajor.$versionMinor.$versionPatch" - const val compileSdk = 34 - const val targetSdk = 34 + const val compileSdk = 35 + const val targetSdk = 35 // When updating the `minSdk`, make sure to update the value of `minSdkVersion` in the file `tools/release/release.sh` val minSdk = if (isEnterpriseBuild) 26 else 24 From 4f60ee2eb23b0187b59bc33911a8d7cfd1ad8cca Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 31 Oct 2024 09:21:43 +0100 Subject: [PATCH 02/50] Fix warning in Versions.kt: const and naming convention. --- app/build.gradle.kts | 6 +++--- plugins/src/main/kotlin/Versions.kt | 8 ++++---- plugins/src/main/kotlin/extension/CommonExtension.kt | 2 +- samples/minimal/build.gradle.kts | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 74e8abcad8..52fb01f067 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -48,9 +48,9 @@ android { } else { "io.element.android.x" } - targetSdk = Versions.targetSdk - versionCode = Versions.versionCode - versionName = Versions.versionName + targetSdk = Versions.TARGET_SDK + versionCode = Versions.VERSION_CODE + versionName = Versions.VERSION_NAME // Keep abiFilter for the universalApk ndk { diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 61df38ebd7..8a14ce5161 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -50,10 +50,10 @@ private const val versionMinor = 7 private const val versionPatch = 3 object Versions { - val versionCode = 4_000_000 + versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch - val versionName = "$versionMajor.$versionMinor.$versionPatch" - const val compileSdk = 35 - const val targetSdk = 35 + const val VERSION_CODE = 4_000_000 + versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch + const val VERSION_NAME = "$versionMajor.$versionMinor.$versionPatch" + const val COMPILE_SDK = 35 + const val TARGET_SDK = 35 // When updating the `minSdk`, make sure to update the value of `minSdkVersion` in the file `tools/release/release.sh` val minSdk = if (isEnterpriseBuild) 26 else 24 diff --git a/plugins/src/main/kotlin/extension/CommonExtension.kt b/plugins/src/main/kotlin/extension/CommonExtension.kt index 4f3f23d707..7c96386a6b 100644 --- a/plugins/src/main/kotlin/extension/CommonExtension.kt +++ b/plugins/src/main/kotlin/extension/CommonExtension.kt @@ -15,7 +15,7 @@ import java.io.File fun CommonExtension<*, *, *, *, *, *>.androidConfig(project: Project) { defaultConfig { - compileSdk = Versions.compileSdk + compileSdk = Versions.COMPILE_SDK minSdk = Versions.minSdk testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/samples/minimal/build.gradle.kts b/samples/minimal/build.gradle.kts index e32054a272..48713af442 100644 --- a/samples/minimal/build.gradle.kts +++ b/samples/minimal/build.gradle.kts @@ -14,9 +14,9 @@ android { defaultConfig { applicationId = "io.element.android.samples.minimal" - targetSdk = Versions.targetSdk - versionCode = Versions.versionCode - versionName = Versions.versionName + targetSdk = Versions.TARGET_SDK + versionCode = Versions.VERSION_CODE + versionName = Versions.VERSION_NAME testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } From 223eae96025d06cd5b5388bb9ca1271bd43e630e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Nov 2024 12:54:11 +0100 Subject: [PATCH 03/50] Send caption with media --- .../preview/AttachmentsPreviewPresenter.kt | 26 +++++- .../preview/AttachmentsPreviewState.kt | 2 + .../AttachmentsPreviewStateProvider.kt | 6 +- .../preview/AttachmentsPreviewView.kt | 80 +++++++++++----- .../MessageComposerPresenter.kt | 2 + .../AttachmentsPreviewPresenterTest.kt | 92 ++++++++++++++++++- .../libraries/matrix/api/room/MatrixRoom.kt | 8 +- .../libraries/matrix/api/timeline/Timeline.kt | 8 +- .../matrix/impl/room/RustMatrixRoom.kt | 12 +-- .../matrix/impl/timeline/RustTimeline.kt | 16 ++-- .../android/libraries/matrix/test/TestData.kt | 1 + .../matrix/test/room/FakeMatrixRoom.kt | 16 ++-- .../matrix/test/timeline/FakeTimeline.kt | 16 ++-- .../libraries/mediaupload/api/MediaSender.kt | 8 +- .../mediaupload/test/FakeMediaPreProcessor.kt | 43 +++++++++ .../libraries/textcomposer/TextComposer.kt | 33 +++++-- .../textcomposer/model/MessageComposerMode.kt | 5 +- .../textcomposer/model/TextEditorState.kt | 2 + .../tests/konsist/KonsistPreviewTest.kt | 1 + 19 files changed, 301 insertions(+), 76 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt index 42468d66cf..57e2ee3ad8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt @@ -9,16 +9,21 @@ package io.element.android.features.messages.impl.attachments.preview import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.core.ProgressCallback +import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder import io.element.android.libraries.mediaupload.api.MediaSender +import io.element.android.libraries.textcomposer.model.TextEditorState +import io.element.android.libraries.textcomposer.model.rememberMarkdownTextEditorState import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -30,6 +35,7 @@ import kotlin.coroutines.coroutineContext class AttachmentsPreviewPresenter @AssistedInject constructor( @Assisted private val attachment: Attachment, private val mediaSender: MediaSender, + private val permalinkBuilder: PermalinkBuilder, ) : Presenter { @AssistedFactory interface Factory { @@ -44,11 +50,24 @@ class AttachmentsPreviewPresenter @AssistedInject constructor( mutableStateOf(SendActionState.Idle) } + val markdownTextEditorState = rememberMarkdownTextEditorState(initialText = null, initialFocus = false) + val textEditorState by rememberUpdatedState( + TextEditorState.Markdown(markdownTextEditorState) + ) + val ongoingSendAttachmentJob = remember { mutableStateOf(null) } fun handleEvents(attachmentsPreviewEvents: AttachmentsPreviewEvents) { when (attachmentsPreviewEvents) { - AttachmentsPreviewEvents.SendAttachment -> ongoingSendAttachmentJob.value = coroutineScope.sendAttachment(attachment, sendActionState) + is AttachmentsPreviewEvents.SendAttachment -> { + val caption = markdownTextEditorState.getMessageMarkdown(permalinkBuilder) + .takeIf { it.isNotEmpty() } + ongoingSendAttachmentJob.value = coroutineScope.sendAttachment( + attachment = attachment, + caption = caption, + sendActionState = sendActionState, + ) + } AttachmentsPreviewEvents.ClearSendState -> { ongoingSendAttachmentJob.value?.let { it.cancel() @@ -62,18 +81,21 @@ class AttachmentsPreviewPresenter @AssistedInject constructor( return AttachmentsPreviewState( attachment = attachment, sendActionState = sendActionState.value, + textEditorState = textEditorState, eventSink = ::handleEvents ) } private fun CoroutineScope.sendAttachment( attachment: Attachment, + caption: String?, sendActionState: MutableState, ) = launch { when (attachment) { is Attachment.Media -> { sendMedia( mediaAttachment = attachment, + caption = caption, sendActionState = sendActionState, ) } @@ -82,6 +104,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor( private suspend fun sendMedia( mediaAttachment: Attachment.Media, + caption: String?, sendActionState: MutableState, ) = runCatching { val context = coroutineContext @@ -96,6 +119,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor( mediaSender.sendMedia( uri = mediaAttachment.localMedia.uri, mimeType = mediaAttachment.localMedia.info.mimeType, + caption = caption, progressCallback = progressCallback ).getOrThrow() }.fold( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt index fc446d60a8..b85ce2c135 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt @@ -9,10 +9,12 @@ package io.element.android.features.messages.impl.attachments.preview import androidx.compose.runtime.Immutable import io.element.android.features.messages.impl.attachments.Attachment +import io.element.android.libraries.textcomposer.model.TextEditorState data class AttachmentsPreviewState( val attachment: Attachment, val sendActionState: SendActionState, + val textEditorState: TextEditorState, val eventSink: (AttachmentsPreviewEvents) -> Unit ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt index 718f9cfbe4..a671619cc4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt @@ -14,6 +14,8 @@ import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.mediaviewer.api.local.MediaInfo import io.element.android.libraries.mediaviewer.api.local.anApkMediaInfo import io.element.android.libraries.mediaviewer.api.local.anImageMediaInfo +import io.element.android.libraries.textcomposer.model.TextEditorState +import io.element.android.libraries.textcomposer.model.aTextEditorStateMarkdown open class AttachmentsPreviewStateProvider : PreviewParameterProvider { override val values: Sequence @@ -27,11 +29,13 @@ open class AttachmentsPreviewStateProvider : PreviewParameterProvider Unit, - onDismiss: () -> Unit, ) { Box( modifier = Modifier .fillMaxSize() .navigationBarsPadding(), - contentAlignment = Alignment.BottomCenter ) { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { - when (attachment) { + when (val attachment = state.attachment) { is Attachment.Media -> { val localMediaViewState = rememberLocalMediaViewState( zoomableState = rememberZoomableState( @@ -137,27 +154,46 @@ private fun AttachmentPreviewContent( } } AttachmentsPreviewBottomActions( - onCancelClick = onDismiss, + state = state, onSendClick = onSendClick, modifier = Modifier .fillMaxWidth() - .background(Color.Black.copy(alpha = 0.7f)) - .padding(horizontal = 24.dp) - .defaultMinSize(minHeight = 80.dp) + .background(ElementTheme.colors.bgCanvasDefault) + .height(IntrinsicSize.Min) + .align(Alignment.BottomCenter) + .imePadding(), ) } } @Composable private fun AttachmentsPreviewBottomActions( - onCancelClick: () -> Unit, + state: AttachmentsPreviewState, onSendClick: () -> Unit, modifier: Modifier = Modifier ) { - ButtonRowMolecule(modifier = modifier) { - TextButton(stringResource(id = CommonStrings.action_cancel), onClick = onCancelClick) - TextButton(stringResource(id = CommonStrings.action_send), onClick = onSendClick) - } + TextComposer( + modifier = modifier, + state = state.textEditorState, + voiceMessageState = VoiceMessageState.Idle, + composerMode = MessageComposerMode.Caption, + onRequestFocus = {}, + onSendMessage = onSendClick, + showTextFormatting = false, + onResetComposerMode = {}, + onAddAttachment = {}, + onDismissTextFormatting = {}, + enableVoiceMessages = false, + onVoiceRecorderEvent = {}, + onVoicePlayerEvent = {}, + onSendVoiceMessage = {}, + onDeleteVoiceMessage = {}, + onReceiveSuggestion = {}, + resolveMentionDisplay = { _, _ -> TextDisplay.Plain }, + onError = {}, + onTyping = {}, + onSelectRichContent = {}, + ) } // Only preview in dark, dark theme is forced on the Node. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index cce78d601e..6f430cbaae 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -436,6 +436,7 @@ class MessageComposerPresenter @Inject constructor( // Reset composer right away resetComposer(markdownTextEditorState, richTextEditorState, fromEdit = capturedMode is MessageComposerMode.Edit) when (capturedMode) { + is MessageComposerMode.Caption, is MessageComposerMode.Normal -> room.sendMessage( body = message.markdown, htmlBody = message.html, @@ -605,6 +606,7 @@ class MessageComposerPresenter @Inject constructor( ): ComposerDraft? { val message = currentComposerMessage(markdownTextEditorState, richTextEditorState, withMentions = false) val draftType = when (val mode = messageComposerContext.composerMode) { + is MessageComposerMode.Caption, is MessageComposerMode.Normal -> ComposerDraftType.NewMessage is MessageComposerMode.Edit -> { mode.eventOrTransactionId.eventId?.let { eventId -> ComposerDraftType.Edit(eventId) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt index d0b4b87794..51318454eb 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt @@ -20,8 +20,13 @@ import io.element.android.features.messages.impl.attachments.preview.SendActionS import io.element.android.features.messages.impl.fixtures.aMediaAttachment import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.media.FileInfo +import io.element.android.libraries.matrix.api.media.ImageInfo +import io.element.android.libraries.matrix.api.media.VideoInfo +import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.test.A_CAPTION import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler +import io.element.android.libraries.matrix.test.permalink.FakePermalinkBuilder import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.mediaupload.api.MediaPreProcessor import io.element.android.libraries.mediaupload.api.MediaSender @@ -30,19 +35,23 @@ import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.lambda.any import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner import java.io.File +@RunWith(RobolectricTestRunner::class) class AttachmentsPreviewPresenterTest { @get:Rule val warmUpRule = WarmUpRule() - private val mediaPreProcessor = FakeMediaPreProcessor() private val mockMediaUrl: Uri = mockk("localMediaUri") @Test @@ -75,6 +84,80 @@ class AttachmentsPreviewPresenterTest { } } + @Test + fun `present - send image with caption success scenario`() = runTest { + val sendImageResult = + lambdaRecorder> { _, _, _, _, _, _ -> + Result.success(FakeMediaUploadHandler()) + } + val mediaPreProcessor = FakeMediaPreProcessor().apply { + givenImageResult() + } + val room = FakeMatrixRoom( + sendImageResult = sendImageResult, + ) + val presenter = createAttachmentsPreviewPresenter( + room = room, + mediaPreProcessor = mediaPreProcessor, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) + initialState.textEditorState.setMarkdown(A_CAPTION) + initialState.eventSink(AttachmentsPreviewEvents.SendAttachment) + assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing) + val successState = awaitItem() + assertThat(successState.sendActionState).isEqualTo(SendActionState.Done) + sendImageResult.assertions().isCalledOnce().with( + any(), + any(), + any(), + value(A_CAPTION), + any(), + any(), + ) + } + } + + @Test + fun `present - send video with caption success scenario`() = runTest { + val sendVideoResult = + lambdaRecorder> { _, _, _, _, _, _ -> + Result.success(FakeMediaUploadHandler()) + } + val mediaPreProcessor = FakeMediaPreProcessor().apply { + givenVideoResult() + } + val room = FakeMatrixRoom( + sendVideoResult = sendVideoResult, + ) + val presenter = createAttachmentsPreviewPresenter( + room = room, + mediaPreProcessor = mediaPreProcessor, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) + initialState.textEditorState.setMarkdown(A_CAPTION) + initialState.eventSink(AttachmentsPreviewEvents.SendAttachment) + assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing) + val successState = awaitItem() + assertThat(successState.sendActionState).isEqualTo(SendActionState.Done) + sendVideoResult.assertions().isCalledOnce().with( + any(), + any(), + any(), + value(A_CAPTION), + any(), + any(), + ) + } + } + @Test fun `present - send media failure scenario`() = runTest { val failure = MediaPreProcessor.Failure(null) @@ -121,11 +204,14 @@ class AttachmentsPreviewPresenterTest { localMedia: LocalMedia = aLocalMedia( uri = mockMediaUrl, ), - room: MatrixRoom = FakeMatrixRoom() + room: MatrixRoom = FakeMatrixRoom(), + permalinkBuilder: PermalinkBuilder = FakePermalinkBuilder(), + mediaPreProcessor: MediaPreProcessor = FakeMediaPreProcessor(), ): AttachmentsPreviewPresenter { return AttachmentsPreviewPresenter( attachment = aMediaAttachment(localMedia), - mediaSender = MediaSender(mediaPreProcessor, room, InMemorySessionPreferencesStore()) + mediaSender = MediaSender(mediaPreProcessor, room, InMemorySessionPreferencesStore()), + permalinkBuilder = permalinkBuilder, ) } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt index fcc1fd1812..3bbbf6fdd4 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt @@ -132,8 +132,8 @@ interface MatrixRoom : Closeable { file: File, thumbnailFile: File?, imageInfo: ImageInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback? ): Result @@ -141,8 +141,8 @@ interface MatrixRoom : Closeable { file: File, thumbnailFile: File?, videoInfo: VideoInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback? ): Result diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt index 085c4d49ea..695fe906c5 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt @@ -75,8 +75,8 @@ interface Timeline : AutoCloseable { file: File, thumbnailFile: File?, imageInfo: ImageInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback? ): Result @@ -84,8 +84,8 @@ interface Timeline : AutoCloseable { file: File, thumbnailFile: File?, videoInfo: VideoInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback? ): Result diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 31d8ae1f43..a80d092145 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -445,22 +445,22 @@ class RustMatrixRoom( file: File, thumbnailFile: File?, imageInfo: ImageInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback?, ): Result { - return liveTimeline.sendImage(file, thumbnailFile, imageInfo, body, formattedBody, progressCallback) + return liveTimeline.sendImage(file, thumbnailFile, imageInfo, caption, formattedCaption, progressCallback) } override suspend fun sendVideo( file: File, thumbnailFile: File?, videoInfo: VideoInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback?, ): Result { - return liveTimeline.sendVideo(file, thumbnailFile, videoInfo, body, formattedBody, progressCallback) + return liveTimeline.sendVideo(file, thumbnailFile, videoInfo, caption, formattedCaption, progressCallback) } override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt index af597a88ab..019f59bfaf 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt @@ -326,8 +326,8 @@ class RustTimeline( file: File, thumbnailFile: File?, imageInfo: ImageInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback?, ): Result { return sendAttachment(listOfNotNull(file, thumbnailFile)) { @@ -335,8 +335,8 @@ class RustTimeline( url = file.path, thumbnailUrl = thumbnailFile?.path, imageInfo = imageInfo.map(), - caption = body, - formattedCaption = formattedBody?.let { + caption = caption, + formattedCaption = formattedCaption?.let { FormattedBody(body = it, format = MessageFormat.Html) }, storeInCache = true, @@ -349,8 +349,8 @@ class RustTimeline( file: File, thumbnailFile: File?, videoInfo: VideoInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback?, ): Result { return sendAttachment(listOfNotNull(file, thumbnailFile)) { @@ -358,8 +358,8 @@ class RustTimeline( url = file.path, thumbnailUrl = thumbnailFile?.path, videoInfo = videoInfo.map(), - caption = body, - formattedCaption = formattedBody?.let { + caption = caption, + formattedCaption = formattedCaption?.let { FormattedBody(body = it, format = MessageFormat.Html) }, storeInCache = true, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt index 83a9b8e5dd..4b4bed0762 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt @@ -61,6 +61,7 @@ const val A_ROOM_RAW_NAME = "A room raw name" const val A_MESSAGE = "Hello world!" const val A_REPLY = "OK, I'll be there!" const val ANOTHER_MESSAGE = "Hello universe!" +const val A_CAPTION = "A media caption" const val A_REDACTION_REASON = "A redaction reason" diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index 24e253311d..4a740c0732 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -321,8 +321,8 @@ class FakeMatrixRoom( file: File, thumbnailFile: File?, imageInfo: ImageInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback? ): Result = simulateLongTask { simulateSendMediaProgress(progressCallback) @@ -330,8 +330,8 @@ class FakeMatrixRoom( file, thumbnailFile, imageInfo, - body, - formattedBody, + caption, + formattedCaption, progressCallback, ) } @@ -340,8 +340,8 @@ class FakeMatrixRoom( file: File, thumbnailFile: File?, videoInfo: VideoInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback? ): Result = simulateLongTask { simulateSendMediaProgress(progressCallback) @@ -349,8 +349,8 @@ class FakeMatrixRoom( file, thumbnailFile, videoInfo, - body, - formattedBody, + caption, + formattedCaption, progressCallback, ) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt index 0395bc2328..ae40a0a51e 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt @@ -131,15 +131,15 @@ class FakeTimeline( file: File, thumbnailFile: File?, imageInfo: ImageInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback?, ): Result = sendImageLambda( file, thumbnailFile, imageInfo, - body, - formattedBody, + caption, + formattedCaption, progressCallback ) @@ -158,15 +158,15 @@ class FakeTimeline( file: File, thumbnailFile: File?, videoInfo: VideoInfo, - body: String?, - formattedBody: String?, + caption: String?, + formattedCaption: String?, progressCallback: ProgressCallback?, ): Result = sendVideoLambda( file, thumbnailFile, videoInfo, - body, - formattedBody, + caption, + formattedCaption, progressCallback ) diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt index a47629d4f2..54f886d302 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt @@ -106,8 +106,8 @@ class MediaSender @Inject constructor( file = uploadInfo.file, thumbnailFile = uploadInfo.thumbnailFile, imageInfo = uploadInfo.imageInfo, - body = caption, - formattedBody = formattedCaption, + caption = caption, + formattedCaption = formattedCaption, progressCallback = progressCallback ) } @@ -116,8 +116,8 @@ class MediaSender @Inject constructor( file = uploadInfo.file, thumbnailFile = uploadInfo.thumbnailFile, videoInfo = uploadInfo.videoInfo, - body = caption, - formattedBody = formattedCaption, + caption = caption, + formattedCaption = formattedCaption, progressCallback = progressCallback ) } diff --git a/libraries/mediaupload/test/src/main/kotlin/io/element/android/libraries/mediaupload/test/FakeMediaPreProcessor.kt b/libraries/mediaupload/test/src/main/kotlin/io/element/android/libraries/mediaupload/test/FakeMediaPreProcessor.kt index 634cb24be7..efb3b77ed8 100644 --- a/libraries/mediaupload/test/src/main/kotlin/io/element/android/libraries/mediaupload/test/FakeMediaPreProcessor.kt +++ b/libraries/mediaupload/test/src/main/kotlin/io/element/android/libraries/mediaupload/test/FakeMediaPreProcessor.kt @@ -11,6 +11,8 @@ import android.net.Uri import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.FileInfo +import io.element.android.libraries.matrix.api.media.ImageInfo +import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.mediaupload.api.MediaPreProcessor import io.element.android.libraries.mediaupload.api.MediaUploadInfo import io.element.android.tests.testutils.simulateLongTask @@ -61,4 +63,45 @@ class FakeMediaPreProcessor : MediaPreProcessor { ) ) } + + fun givenImageResult() { + givenResult( + Result.success( + MediaUploadInfo.Image( + file = File("image.jpg"), + imageInfo = ImageInfo( + height = 100, + width = 100, + mimetype = MimeTypes.Jpeg, + size = 1000, + thumbnailInfo = null, + thumbnailSource = null, + blurhash = null, + ), + thumbnailFile = null, + ) + ) + ) + } + + fun givenVideoResult() { + givenResult( + Result.success( + MediaUploadInfo.Video( + file = File("image.jpg"), + videoInfo = VideoInfo( + duration = 1000.seconds, + height = 100, + width = 100, + mimetype = MimeTypes.Mp4, + size = 1000, + thumbnailInfo = null, + thumbnailSource = null, + blurhash = null, + ), + thumbnailFile = null, + ) + ) + ) + } } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index 7af05baaa4..ca0d71d4d3 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -125,16 +125,22 @@ fun TextComposer( val composerOptionsButton: @Composable () -> Unit = remember { @Composable { - ComposerOptionsButton( - modifier = Modifier - .size(48.dp), - onClick = onAddAttachment - ) + if (composerMode == MessageComposerMode.Caption) { + Spacer(modifier = Modifier.width(9.dp)) + } else { + ComposerOptionsButton( + modifier = Modifier + .size(48.dp), + onClick = onAddAttachment + ) + } } } val placeholder = if (composerMode.inThread) { stringResource(id = CommonStrings.action_reply_in_thread) + } else if (composerMode == MessageComposerMode.Caption) { + stringResource(id = R.string.rich_text_editor_composer_caption_placeholder) } else { stringResource(id = R.string.rich_text_editor_composer_placeholder) } @@ -180,7 +186,7 @@ fun TextComposer( } } - val canSendMessage = markdown.isNotBlank() + val canSendMessage = markdown.isNotBlank() || composerMode == MessageComposerMode.Caption val sendButton = @Composable { SendButton( canSendMessage = canSendMessage, @@ -592,6 +598,21 @@ internal fun TextComposerReplyPreview(@PreviewParameter(InReplyToDetailsProvider } } +@PreviewsDayNight +@Composable +internal fun TextComposerCaptionPreview(@PreviewParameter(InReplyToDetailsProvider::class) inReplyToDetails: InReplyToDetails) = ElementPreview { + PreviewColumn( + items = aTextEditorStateMarkdownList() + ) { textEditorState -> + ATextComposer( + state = textEditorState, + voiceMessageState = VoiceMessageState.Idle, + composerMode = MessageComposerMode.Caption, + enableVoiceMessages = false, + ) + } +} + @PreviewsDayNight @Composable internal fun TextComposerVoicePreview() = ElementPreview { diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt index ef96000d2b..a729f332b7 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt @@ -18,6 +18,8 @@ import io.element.android.libraries.matrix.ui.messages.reply.eventId sealed interface MessageComposerMode { data object Normal : MessageComposerMode + data object Caption : MessageComposerMode + sealed interface Special : MessageComposerMode data class Edit( @@ -34,7 +36,8 @@ sealed interface MessageComposerMode { val relatedEventId: EventId? get() = when (this) { - is Normal -> null + is Normal, + is Caption -> null is Edit -> eventOrTransactionId.eventId is Reply -> eventId } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/TextEditorState.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/TextEditorState.kt index 88c970d848..b1f7d56d07 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/TextEditorState.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/TextEditorState.kt @@ -36,6 +36,7 @@ sealed interface TextEditorState { is Rich -> richTextEditorState.hasFocus } + // Note: for test only suspend fun setHtml(html: String) { when (this) { is Markdown -> Unit @@ -43,6 +44,7 @@ sealed interface TextEditorState { } } + // Note: for test only suspend fun setMarkdown(text: String) { when (this) { is Markdown -> state.text.update(text, true) diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt index c00d72fffb..83541624a1 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt @@ -101,6 +101,7 @@ class KonsistPreviewTest { "SasEmojisPreview", "SecureBackupSetupViewChangePreview", "SelectedUserCannotRemovePreview", + "TextComposerCaptionPreview", "TextComposerEditPreview", "TextComposerFormattingPreview", "TextComposerLinkDialogCreateLinkPreview", From 39ab2f848ac2012067428db7593fb4c9dc4eeb7b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Nov 2024 13:46:01 +0100 Subject: [PATCH 04/50] Fix multiple previews issue. --- .../libraries/textcomposer/TextComposer.kt | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index ca0d71d4d3..47f4b5268f 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -120,8 +120,8 @@ fun TextComposer( } val layoutModifier = modifier - .fillMaxSize() - .height(IntrinsicSize.Min) + .fillMaxSize() + .height(IntrinsicSize.Min) val composerOptionsButton: @Composable () -> Unit = remember { @Composable { @@ -324,8 +324,8 @@ private fun StandardLayout( if (voiceMessageState is VoiceMessageState.Preview || voiceMessageState is VoiceMessageState.Recording) { Box( modifier = Modifier - .padding(bottom = 5.dp, top = 5.dp, end = 3.dp, start = 3.dp) - .size(48.dp), + .padding(bottom = 5.dp, top = 5.dp, end = 3.dp, start = 3.dp) + .size(48.dp), contentAlignment = Alignment.Center, ) { voiceDeleteButton() @@ -335,8 +335,8 @@ private fun StandardLayout( } Box( modifier = Modifier - .padding(bottom = 8.dp, top = 8.dp) - .weight(1f) + .padding(bottom = 8.dp, top = 8.dp) + .weight(1f) ) { voiceRecording() } @@ -349,16 +349,16 @@ private fun StandardLayout( } Box( modifier = Modifier - .padding(bottom = 8.dp, top = 8.dp) - .weight(1f) + .padding(bottom = 8.dp, top = 8.dp) + .weight(1f) ) { textInput() } } Box( - Modifier - .padding(bottom = 5.dp, top = 5.dp, end = 6.dp, start = 6.dp) - .size(48.dp), + Modifier + .padding(bottom = 5.dp, top = 5.dp, end = 6.dp, start = 6.dp) + .size(48.dp), contentAlignment = Alignment.Center, ) { endButton() @@ -380,8 +380,8 @@ private fun TextFormattingLayout( ) { Box( modifier = Modifier - .weight(1f) - .padding(horizontal = 12.dp) + .weight(1f) + .padding(horizontal = 12.dp) ) { textInput() } @@ -425,11 +425,11 @@ private fun TextInputBox( Column( modifier = Modifier - .clip(roundedCorners) - .border(0.5.dp, borderColor, roundedCorners) - .background(color = bgColor) - .requiredHeightIn(min = 42.dp) - .fillMaxSize(), + .clip(roundedCorners) + .border(0.5.dp, borderColor, roundedCorners) + .background(color = bgColor) + .requiredHeightIn(min = 42.dp) + .fillMaxSize(), ) { if (composerMode is MessageComposerMode.Special) { ComposerModeView( @@ -440,9 +440,9 @@ private fun TextInputBox( val defaultTypography = ElementTheme.typography.fontBodyLgRegular Box( modifier = Modifier - .padding(top = 4.dp, bottom = 4.dp, start = 12.dp, end = 12.dp) - // Apply test tag only once, otherwise 2 nodes will have it (both the normal and subcomposing one) and tests will fail - .then(if (!subcomposing) Modifier.testTag(TestTags.textEditor) else Modifier), + .padding(top = 4.dp, bottom = 4.dp, start = 12.dp, end = 12.dp) + // Apply test tag only once, otherwise 2 nodes will have it (both the normal and subcomposing one) and tests will fail + .then(if (!subcomposing) Modifier.testTag(TestTags.textEditor) else Modifier), contentAlignment = Alignment.CenterStart, ) { // Placeholder @@ -488,8 +488,8 @@ private fun TextInput( // This prevents it gaining focus and mutating the state. registerStateUpdates = !subcomposing, modifier = Modifier - .padding(top = 6.dp, bottom = 6.dp) - .fillMaxWidth(), + .padding(top = 6.dp, bottom = 6.dp) + .fillMaxWidth(), style = ElementRichTextEditorStyle.composerStyle(hasFocus = state.hasFocus), resolveMentionDisplay = resolveMentionDisplay, resolveRoomMentionDisplay = resolveRoomMentionDisplay, @@ -600,7 +600,7 @@ internal fun TextComposerReplyPreview(@PreviewParameter(InReplyToDetailsProvider @PreviewsDayNight @Composable -internal fun TextComposerCaptionPreview(@PreviewParameter(InReplyToDetailsProvider::class) inReplyToDetails: InReplyToDetails) = ElementPreview { +internal fun TextComposerCaptionPreview() = ElementPreview { PreviewColumn( items = aTextEditorStateMarkdownList() ) { textEditorState -> From 19eb4c8395857ffa8befe41858d187815e268372 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Nov 2024 14:09:27 +0100 Subject: [PATCH 05/50] Do not allow caption on audio files. Regular files are not previewed, but prevent caption as well there. --- .../preview/AttachmentsPreviewState.kt | 9 +- .../preview/AttachmentsPreviewView.kt | 2 +- .../MessageComposerPresenter.kt | 4 +- .../libraries/textcomposer/TextComposer.kt | 152 +++++++++--------- .../textcomposer/model/MessageComposerMode.kt | 4 +- 5 files changed, 93 insertions(+), 78 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt index b85ce2c135..72ea0a2098 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt @@ -9,6 +9,9 @@ package io.element.android.features.messages.impl.attachments.preview import androidx.compose.runtime.Immutable import io.element.android.features.messages.impl.attachments.Attachment +import io.element.android.libraries.core.bool.orFalse +import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage +import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo import io.element.android.libraries.textcomposer.model.TextEditorState data class AttachmentsPreviewState( @@ -16,7 +19,11 @@ data class AttachmentsPreviewState( val sendActionState: SendActionState, val textEditorState: TextEditorState, val eventSink: (AttachmentsPreviewEvents) -> Unit -) +) { + val allowCaption: Boolean = (attachment as? Attachment.Media)?.localMedia?.info?.mimeType?.let { + it.isMimeTypeImage() || it.isMimeTypeVideo() + }.orFalse() +} @Immutable sealed interface SendActionState { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt index ab7a117fdf..2906f26b3c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt @@ -176,7 +176,7 @@ private fun AttachmentsPreviewBottomActions( modifier = modifier, state = state.textEditorState, voiceMessageState = VoiceMessageState.Idle, - composerMode = MessageComposerMode.Caption, + composerMode = MessageComposerMode.Attachment(state.allowCaption), onRequestFocus = {}, onSendMessage = onSendClick, showTextFormatting = false, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index 6f430cbaae..6101f48c1f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -436,7 +436,7 @@ class MessageComposerPresenter @Inject constructor( // Reset composer right away resetComposer(markdownTextEditorState, richTextEditorState, fromEdit = capturedMode is MessageComposerMode.Edit) when (capturedMode) { - is MessageComposerMode.Caption, + is MessageComposerMode.Attachment, is MessageComposerMode.Normal -> room.sendMessage( body = message.markdown, htmlBody = message.html, @@ -606,7 +606,7 @@ class MessageComposerPresenter @Inject constructor( ): ComposerDraft? { val message = currentComposerMessage(markdownTextEditorState, richTextEditorState, withMentions = false) val draftType = when (val mode = messageComposerContext.composerMode) { - is MessageComposerMode.Caption, + is MessageComposerMode.Attachment, is MessageComposerMode.Normal -> ComposerDraftType.NewMessage is MessageComposerMode.Edit -> { mode.eventOrTransactionId.eventId?.let { eventId -> ComposerDraftType.Edit(eventId) } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index 47f4b5268f..eca7340219 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -72,6 +72,7 @@ import io.element.android.wysiwyg.compose.RichTextEditorState import io.element.android.wysiwyg.display.TextDisplay import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList import uniffi.wysiwyg_composer.MenuAction import kotlin.time.Duration.Companion.seconds @@ -120,12 +121,12 @@ fun TextComposer( } val layoutModifier = modifier - .fillMaxSize() - .height(IntrinsicSize.Min) + .fillMaxSize() + .height(IntrinsicSize.Min) val composerOptionsButton: @Composable () -> Unit = remember { @Composable { - if (composerMode == MessageComposerMode.Caption) { + if (composerMode is MessageComposerMode.Attachment) { Spacer(modifier = Modifier.width(9.dp)) } else { ComposerOptionsButton( @@ -139,54 +140,60 @@ fun TextComposer( val placeholder = if (composerMode.inThread) { stringResource(id = CommonStrings.action_reply_in_thread) - } else if (composerMode == MessageComposerMode.Caption) { + } else if (composerMode is MessageComposerMode.Attachment) { stringResource(id = R.string.rich_text_editor_composer_caption_placeholder) } else { stringResource(id = R.string.rich_text_editor_composer_placeholder) } - val textInput: @Composable () -> Unit = when (state) { - is TextEditorState.Rich -> { - remember(state.richTextEditorState, subcomposing, composerMode, onResetComposerMode, onError) { - @Composable { - TextInput( - state = state.richTextEditorState, - subcomposing = subcomposing, - placeholder = placeholder, - composerMode = composerMode, - onResetComposerMode = onResetComposerMode, - resolveMentionDisplay = resolveMentionDisplay, - resolveRoomMentionDisplay = { resolveMentionDisplay("@room", "#") }, - onError = onError, - onTyping = onTyping, - onSelectRichContent = onSelectRichContent, - ) + val textInput: @Composable () -> Unit = if ((composerMode as? MessageComposerMode.Attachment)?.allowCaption == false) { + { + // No text input when in attachment mode and caption not allowed. + } + } else { + when (state) { + is TextEditorState.Rich -> { + remember(state.richTextEditorState, subcomposing, composerMode, onResetComposerMode, onError) { + @Composable { + TextInput( + state = state.richTextEditorState, + subcomposing = subcomposing, + placeholder = placeholder, + composerMode = composerMode, + onResetComposerMode = onResetComposerMode, + resolveMentionDisplay = resolveMentionDisplay, + resolveRoomMentionDisplay = { resolveMentionDisplay("@room", "#") }, + onError = onError, + onTyping = onTyping, + onSelectRichContent = onSelectRichContent, + ) + } } } - } - is TextEditorState.Markdown -> { - @Composable { - val style = ElementRichTextEditorStyle.composerStyle(hasFocus = state.hasFocus()) - TextInputBox( - composerMode = composerMode, - onResetComposerMode = onResetComposerMode, - placeholder = placeholder, - showPlaceholder = { state.state.text.value().isEmpty() }, - subcomposing = subcomposing, - ) { - MarkdownTextInput( - state = state.state, + is TextEditorState.Markdown -> { + @Composable { + val style = ElementRichTextEditorStyle.composerStyle(hasFocus = state.hasFocus()) + TextInputBox( + composerMode = composerMode, + onResetComposerMode = onResetComposerMode, + placeholder = placeholder, + showPlaceholder = { state.state.text.value().isEmpty() }, subcomposing = subcomposing, - onTyping = onTyping, - onReceiveSuggestion = onReceiveSuggestion, - richTextEditorStyle = style, - onSelectRichContent = onSelectRichContent, - ) + ) { + MarkdownTextInput( + state = state.state, + subcomposing = subcomposing, + onTyping = onTyping, + onReceiveSuggestion = onReceiveSuggestion, + richTextEditorStyle = style, + onSelectRichContent = onSelectRichContent, + ) + } } } } } - val canSendMessage = markdown.isNotBlank() || composerMode == MessageComposerMode.Caption + val canSendMessage = markdown.isNotBlank() || composerMode is MessageComposerMode.Attachment val sendButton = @Composable { SendButton( canSendMessage = canSendMessage, @@ -324,8 +331,8 @@ private fun StandardLayout( if (voiceMessageState is VoiceMessageState.Preview || voiceMessageState is VoiceMessageState.Recording) { Box( modifier = Modifier - .padding(bottom = 5.dp, top = 5.dp, end = 3.dp, start = 3.dp) - .size(48.dp), + .padding(bottom = 5.dp, top = 5.dp, end = 3.dp, start = 3.dp) + .size(48.dp), contentAlignment = Alignment.Center, ) { voiceDeleteButton() @@ -335,8 +342,8 @@ private fun StandardLayout( } Box( modifier = Modifier - .padding(bottom = 8.dp, top = 8.dp) - .weight(1f) + .padding(bottom = 8.dp, top = 8.dp) + .weight(1f) ) { voiceRecording() } @@ -349,16 +356,16 @@ private fun StandardLayout( } Box( modifier = Modifier - .padding(bottom = 8.dp, top = 8.dp) - .weight(1f) + .padding(bottom = 8.dp, top = 8.dp) + .weight(1f) ) { textInput() } } Box( - Modifier - .padding(bottom = 5.dp, top = 5.dp, end = 6.dp, start = 6.dp) - .size(48.dp), + Modifier + .padding(bottom = 5.dp, top = 5.dp, end = 6.dp, start = 6.dp) + .size(48.dp), contentAlignment = Alignment.Center, ) { endButton() @@ -380,8 +387,8 @@ private fun TextFormattingLayout( ) { Box( modifier = Modifier - .weight(1f) - .padding(horizontal = 12.dp) + .weight(1f) + .padding(horizontal = 12.dp) ) { textInput() } @@ -425,11 +432,11 @@ private fun TextInputBox( Column( modifier = Modifier - .clip(roundedCorners) - .border(0.5.dp, borderColor, roundedCorners) - .background(color = bgColor) - .requiredHeightIn(min = 42.dp) - .fillMaxSize(), + .clip(roundedCorners) + .border(0.5.dp, borderColor, roundedCorners) + .background(color = bgColor) + .requiredHeightIn(min = 42.dp) + .fillMaxSize(), ) { if (composerMode is MessageComposerMode.Special) { ComposerModeView( @@ -440,9 +447,9 @@ private fun TextInputBox( val defaultTypography = ElementTheme.typography.fontBodyLgRegular Box( modifier = Modifier - .padding(top = 4.dp, bottom = 4.dp, start = 12.dp, end = 12.dp) - // Apply test tag only once, otherwise 2 nodes will have it (both the normal and subcomposing one) and tests will fail - .then(if (!subcomposing) Modifier.testTag(TestTags.textEditor) else Modifier), + .padding(top = 4.dp, bottom = 4.dp, start = 12.dp, end = 12.dp) + // Apply test tag only once, otherwise 2 nodes will have it (both the normal and subcomposing one) and tests will fail + .then(if (!subcomposing) Modifier.testTag(TestTags.textEditor) else Modifier), contentAlignment = Alignment.CenterStart, ) { // Placeholder @@ -488,8 +495,8 @@ private fun TextInput( // This prevents it gaining focus and mutating the state. registerStateUpdates = !subcomposing, modifier = Modifier - .padding(top = 6.dp, bottom = 6.dp) - .fillMaxWidth(), + .padding(top = 6.dp, bottom = 6.dp) + .fillMaxWidth(), style = ElementRichTextEditorStyle.composerStyle(hasFocus = state.hasFocus), resolveMentionDisplay = resolveMentionDisplay, resolveRoomMentionDisplay = resolveRoomMentionDisplay, @@ -525,7 +532,7 @@ private fun aTextEditorStateRichList() = persistentListOf( internal fun TextComposerSimplePreview() = ElementPreview { PreviewColumn( items = aTextEditorStateMarkdownList() - ) { textEditorState -> + ) { _, textEditorState -> ATextComposer( state = textEditorState, voiceMessageState = VoiceMessageState.Idle, @@ -540,7 +547,7 @@ internal fun TextComposerSimplePreview() = ElementPreview { internal fun TextComposerFormattingPreview() = ElementPreview { PreviewColumn( items = aTextEditorStateRichList() - ) { textEditorState -> + ) { _, textEditorState -> ATextComposer( state = textEditorState, voiceMessageState = VoiceMessageState.Idle, @@ -556,7 +563,7 @@ internal fun TextComposerFormattingPreview() = ElementPreview { internal fun TextComposerEditPreview() = ElementPreview { PreviewColumn( items = aTextEditorStateRichList() - ) { textEditorState -> + ) { _, textEditorState -> ATextComposer( state = textEditorState, voiceMessageState = VoiceMessageState.Idle, @@ -571,7 +578,7 @@ internal fun TextComposerEditPreview() = ElementPreview { internal fun MarkdownTextComposerEditPreview() = ElementPreview { PreviewColumn( items = aTextEditorStateMarkdownList() - ) { textEditorState -> + ) { _, textEditorState -> ATextComposer( state = textEditorState, voiceMessageState = VoiceMessageState.Idle, @@ -586,7 +593,7 @@ internal fun MarkdownTextComposerEditPreview() = ElementPreview { internal fun TextComposerReplyPreview(@PreviewParameter(InReplyToDetailsProvider::class) inReplyToDetails: InReplyToDetails) = ElementPreview { PreviewColumn( items = aTextEditorStateRichList() - ) { textEditorState -> + ) { _, textEditorState -> ATextComposer( state = textEditorState, voiceMessageState = VoiceMessageState.Idle, @@ -601,13 +608,14 @@ internal fun TextComposerReplyPreview(@PreviewParameter(InReplyToDetailsProvider @PreviewsDayNight @Composable internal fun TextComposerCaptionPreview() = ElementPreview { + val list = aTextEditorStateMarkdownList() PreviewColumn( - items = aTextEditorStateMarkdownList() - ) { textEditorState -> + items = (list + aTextEditorStateMarkdown(initialText = "NO_CAPTION", initialFocus = true)).toPersistentList() + ) { index, textEditorState -> ATextComposer( state = textEditorState, voiceMessageState = VoiceMessageState.Idle, - composerMode = MessageComposerMode.Caption, + composerMode = MessageComposerMode.Attachment(allowCaption = index < list.size), enableVoiceMessages = false, ) } @@ -644,7 +652,7 @@ internal fun TextComposerVoicePreview() = ElementPreview { playbackProgress = 0.0f ), ) - ) { voiceMessageState -> + ) { _, voiceMessageState -> ATextComposer( state = aTextEditorStateRich(initialFocus = true), voiceMessageState = voiceMessageState, @@ -657,14 +665,14 @@ internal fun TextComposerVoicePreview() = ElementPreview { @Composable private fun PreviewColumn( items: ImmutableList, - view: @Composable (T) -> Unit, + view: @Composable (Int, T) -> Unit, ) { Column { - items.forEach { item -> + items.forEachIndexed { index, item -> Box( modifier = Modifier.height(IntrinsicSize.Min) ) { - view(item) + view(index, item) } } } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt index a729f332b7..1915359c83 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt @@ -18,7 +18,7 @@ import io.element.android.libraries.matrix.ui.messages.reply.eventId sealed interface MessageComposerMode { data object Normal : MessageComposerMode - data object Caption : MessageComposerMode + data class Attachment(val allowCaption: Boolean) : MessageComposerMode sealed interface Special : MessageComposerMode @@ -37,7 +37,7 @@ sealed interface MessageComposerMode { val relatedEventId: EventId? get() = when (this) { is Normal, - is Caption -> null + is Attachment -> null is Edit -> eventOrTransactionId.eventId is Reply -> eventId } From a17bedc457f53e08c554068a82b3f443c8881881 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Nov 2024 14:24:00 +0100 Subject: [PATCH 06/50] Add more preview. --- .../attachments/preview/AttachmentsPreviewStateProvider.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt index a671619cc4..78f3ffc81a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt @@ -12,7 +12,9 @@ import androidx.core.net.toUri import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.mediaviewer.api.local.MediaInfo +import io.element.android.libraries.mediaviewer.api.local.aVideoMediaInfo import io.element.android.libraries.mediaviewer.api.local.anApkMediaInfo +import io.element.android.libraries.mediaviewer.api.local.anAudioMediaInfo import io.element.android.libraries.mediaviewer.api.local.anImageMediaInfo import io.element.android.libraries.textcomposer.model.TextEditorState import io.element.android.libraries.textcomposer.model.aTextEditorStateMarkdown @@ -21,6 +23,8 @@ open class AttachmentsPreviewStateProvider : PreviewParameterProvider get() = sequenceOf( anAttachmentsPreviewState(), + anAttachmentsPreviewState(mediaInfo = aVideoMediaInfo()), + anAttachmentsPreviewState(mediaInfo = anAudioMediaInfo()), anAttachmentsPreviewState(mediaInfo = anApkMediaInfo()), anAttachmentsPreviewState(sendActionState = SendActionState.Sending.Uploading(0.5f)), anAttachmentsPreviewState(sendActionState = SendActionState.Failure(RuntimeException("error"))), From ddc40857422e28fdb0986c7ba3d02073f24e6ed7 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Mon, 4 Nov 2024 13:45:11 +0000 Subject: [PATCH 07/50] Update screenshots --- ...messages.impl.attachments.preview_AttachmentsView_0_en.png | 4 ++-- ...messages.impl.attachments.preview_AttachmentsView_1_en.png | 4 ++-- ...messages.impl.attachments.preview_AttachmentsView_2_en.png | 4 ++-- ...messages.impl.attachments.preview_AttachmentsView_3_en.png | 4 ++-- ...messages.impl.attachments.preview_AttachmentsView_4_en.png | 3 +++ ...messages.impl.attachments.preview_AttachmentsView_5_en.png | 3 +++ .../libraries.textcomposer_TextComposerCaption_Day_0_en.png | 3 +++ .../libraries.textcomposer_TextComposerCaption_Night_0_en.png | 3 +++ 8 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_4_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_5_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerCaption_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerCaption_Night_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_0_en.png index 622f3dc9c1..dd26109be0 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b8b87bd63d1f6febead491dda1cecc0fa9fad0cdca317cf29086fbeec6a9231 -size 390555 +oid sha256:899eff34c421e13bf62d6828582c715f87588cfb17c1063aae65baae79a472cf +size 394631 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_1_en.png index 438fe60e31..7514170564 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ba4294693123669a24fbdec8093d3c572c639325763c15a1aa53c8f1e4a7659 -size 15230 +oid sha256:c41c7438f46e62a4a6f115647040005aba9fd057599fbb17aba844337642d525 +size 15963 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_2_en.png index 68bbcc5e4c..b5ca13b59e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16c19e5b2a95da604052a86bd3cdb00d4260ee7539d79e8bb96784e2a2920836 -size 47019 +oid sha256:4a9d51bdba64cbd7c453ac177e9c77fb6aa3c611ac16746701b22b024abf3560 +size 13770 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_3_en.png index 8c3ad63622..d9ea161fab 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a42e8f411d6e54656104cf9472a00713b150ee9978b8bde26b22acd858478bfc -size 84755 +oid sha256:9be7c12e6de6bd2975f11ff06eab1b6fa973edcda0ca90c93eed164cb1d6bf18 +size 14841 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_4_en.png new file mode 100644 index 0000000000..5f643fefc6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9434a531c57fa65c9996f5b5c6254af73ac50433ceeeaa7f1dd1243fe3c3b1c6 +size 50355 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_5_en.png new file mode 100644 index 0000000000..ac695699f0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsView_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1c36d5b0d29f1533829f80c33e042bb88648890ad2b629136f8a2af01c511f7a +size 87977 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerCaption_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerCaption_Day_0_en.png new file mode 100644 index 0000000000..151c86e357 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerCaption_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:391d3741acfd768614a9bc70e948f7fc49b37d75e65591721b922e678d520bac +size 44773 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerCaption_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerCaption_Night_0_en.png new file mode 100644 index 0000000000..91dfb69902 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerCaption_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:713a314ecd36e4d95e5c287ab9a4b5968f5a5090dbb4910d9739e66969fdd424 +size 43417 From 8c989f716a584e685d80497314369d41446ca247 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 25 Oct 2024 18:27:23 +0200 Subject: [PATCH 08/50] create room : start adding new options in ui --- .../createroom/impl/CreateRoomConfig.kt | 4 +- .../createroom/impl/CreateRoomDataStore.kt | 83 +++++++++- .../impl/components/RoomAccessOption.kt | 87 ++++++++++ ...ivacyOption.kt => RoomVisibilityOption.kt} | 51 +++--- .../impl/configureroom/ConfigureRoomEvents.kt | 6 +- .../configureroom/ConfigureRoomPresenter.kt | 15 +- .../ConfigureRoomStateProvider.kt | 5 +- .../impl/configureroom/ConfigureRoomView.kt | 156 ++++++++++++++++-- .../{RoomPrivacy.kt => RoomAccess.kt} | 7 +- .../impl/configureroom/RoomAccessItem.kt | 26 +++ .../impl/configureroom/RoomAddress.kt | 13 ++ .../impl/configureroom/RoomPrivacyItem.kt | 45 ----- .../impl/configureroom/RoomVisibilityItem.kt | 30 ++++ .../impl/configureroom/RoomVisibilityState.kt | 21 +++ .../ConfigureRoomPresenterTest.kt | 8 +- 15 files changed, 447 insertions(+), 110 deletions(-) create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomAccessOption.kt rename features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/{RoomPrivacyOption.kt => RoomVisibilityOption.kt} (68%) rename features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/{RoomPrivacy.kt => RoomAccess.kt} (67%) create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccessItem.kt create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddress.kt delete mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityItem.kt create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityState.kt diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomConfig.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomConfig.kt index 731dc27d07..a83673886d 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomConfig.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomConfig.kt @@ -8,7 +8,7 @@ package io.element.android.features.createroom.impl import android.net.Uri -import io.element.android.features.createroom.impl.configureroom.RoomPrivacy +import io.element.android.features.createroom.impl.configureroom.RoomVisibilityState import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -18,5 +18,5 @@ data class CreateRoomConfig( val topic: String? = null, val avatarUri: Uri? = null, val invites: ImmutableList = persistentListOf(), - val privacy: RoomPrivacy = RoomPrivacy.Private, + val roomVisibility: RoomVisibilityState = RoomVisibilityState.Private, ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt index 5925ca7818..17d7d5cc3d 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt @@ -8,7 +8,11 @@ package io.element.android.features.createroom.impl import android.net.Uri -import io.element.android.features.createroom.impl.configureroom.RoomPrivacy +import io.element.android.features.createroom.impl.configureroom.RoomAccess +import io.element.android.features.createroom.impl.configureroom.RoomAccessItem +import io.element.android.features.createroom.impl.configureroom.RoomAddress +import io.element.android.features.createroom.impl.configureroom.RoomVisibilityItem +import io.element.android.features.createroom.impl.configureroom.RoomVisibilityState import io.element.android.features.createroom.impl.di.CreateRoomScope import io.element.android.features.createroom.impl.userlist.UserListDataStore import io.element.android.libraries.androidutils.file.safeDelete @@ -17,7 +21,9 @@ import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.getAndUpdate import java.io.File +import java.text.Normalizer import javax.inject.Inject @SingleIn(CreateRoomScope::class) @@ -31,28 +37,87 @@ class CreateRoomDataStore @Inject constructor( field = value } - fun getCreateRoomConfig(): Flow = combine( + val createRoomConfig: Flow = combine( selectedUserListDataStore.selectedUsers(), createRoomConfigFlow, ) { selectedUsers, config -> config.copy(invites = selectedUsers.toImmutableList()) } - fun setRoomName(roomName: String?) { - createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(roomName = roomName?.takeIf { it.isNotEmpty() })) + fun setRoomName(roomName: String) { + createRoomConfigFlow.getAndUpdate { config -> + val newVisibility = when (config.roomVisibility) { + is RoomVisibilityState.Public -> { + val roomAddress = config.roomVisibility.roomAddress + if (roomAddress is RoomAddress.AutoFilled || roomName.isEmpty()) { + config.roomVisibility.copy( + roomAddress = RoomAddress.AutoFilled(roomName), + ) + } else { + config.roomVisibility + } + } + else -> config.roomVisibility + } + config.copy( + roomName = roomName.takeIf { it.isNotEmpty() }, + roomVisibility = newVisibility, + ) + } } - fun setTopic(topic: String?) { - createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(topic = topic?.takeIf { it.isNotEmpty() })) + fun setTopic(topic: String) { + createRoomConfigFlow.getAndUpdate { config -> + config.copy(topic = topic.takeIf { it.isNotEmpty() }) + } } fun setAvatarUri(uri: Uri?, cached: Boolean = false) { cachedAvatarUri = uri.takeIf { cached } - createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(avatarUri = uri)) + createRoomConfigFlow.getAndUpdate { config -> + config.copy(avatarUri = uri) + } } - fun setPrivacy(privacy: RoomPrivacy) { - createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(privacy = privacy)) + fun setRoomVisibility(visibility: RoomVisibilityItem) { + createRoomConfigFlow.getAndUpdate { config -> + config.copy( + roomVisibility = when (visibility) { + RoomVisibilityItem.Private -> RoomVisibilityState.Private + RoomVisibilityItem.Public -> RoomVisibilityState.Public( + roomAddress = RoomAddress.AutoFilled(config.roomName.orEmpty()), + roomAccess = RoomAccess.Anyone, + ) + } + ) + } + } + + fun setRoomAddress(address: String) { + createRoomConfigFlow.getAndUpdate { config -> + config.copy( + roomVisibility = when (config.roomVisibility) { + is RoomVisibilityState.Public -> config.roomVisibility.copy(roomAddress = RoomAddress.Edited(address)) + else -> config.roomVisibility + } + ) + } + } + + fun setRoomAccess(access: RoomAccessItem) { + createRoomConfigFlow.getAndUpdate { config -> + config.copy( + roomVisibility = when (config.roomVisibility) { + is RoomVisibilityState.Public -> { + when (access) { + RoomAccessItem.Anyone -> config.roomVisibility.copy(roomAccess = RoomAccess.Anyone) + RoomAccessItem.AskToJoin -> config.roomVisibility.copy(roomAccess = RoomAccess.Knocking) + } + } + else -> config.roomVisibility + } + ) + } } fun clearCachedData() { diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomAccessOption.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomAccessOption.kt new file mode 100644 index 0000000000..4d39f698be --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomAccessOption.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.createroom.impl.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.selection.selectable +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.features.createroom.impl.configureroom.RoomAccessItem +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.RadioButton +import io.element.android.libraries.designsystem.theme.components.Text + +@Composable +fun RoomAccessOption( + roomAccessItem: RoomAccessItem, + onOptionClick: (RoomAccessItem) -> Unit, + modifier: Modifier = Modifier, + isSelected: Boolean = false, +) { + Row( + modifier + .fillMaxWidth() + .selectable( + selected = isSelected, + onClick = { onOptionClick(roomAccessItem) }, + role = Role.RadioButton, + ) + ) { + Column(Modifier.weight(1f)) { + Text( + text = stringResource(roomAccessItem.title), + style = ElementTheme.typography.fontBodyLgRegular, + color = MaterialTheme.colorScheme.primary, + ) + Spacer(Modifier.size(8.dp)) + Text( + text = stringResource(roomAccessItem.description), + style = ElementTheme.typography.fontBodySmRegular, + color = MaterialTheme.colorScheme.tertiary, + ) + } + RadioButton( + modifier = Modifier + .align(Alignment.CenterVertically) + .size(48.dp), + selected = isSelected, + // null recommended for accessibility with screenreaders + onClick = null + ) + } +} + +@PreviewsDayNight +@Composable +internal fun RoomAccessOptionPreview() = ElementPreview { + val aRoomAccessItem = RoomAccessItem.Anyone + Column { + RoomAccessOption( + roomAccessItem = aRoomAccessItem, + onOptionClick = {}, + isSelected = true, + ) + RoomAccessOption( + roomAccessItem = aRoomAccessItem, + onOptionClick = {}, + isSelected = false, + ) + } +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt similarity index 68% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt rename to features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt index 302f6134e6..cf6ab077a4 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt @@ -7,6 +7,8 @@ package io.element.android.features.createroom.impl.components +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -14,15 +16,17 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme -import io.element.android.features.createroom.impl.configureroom.RoomPrivacyItem -import io.element.android.features.createroom.impl.configureroom.roomPrivacyItems +import io.element.android.features.createroom.impl.configureroom.RoomVisibilityItem 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 @@ -30,9 +34,9 @@ import io.element.android.libraries.designsystem.theme.components.RadioButton import io.element.android.libraries.designsystem.theme.components.Text @Composable -fun RoomPrivacyOption( - roomPrivacyItem: RoomPrivacyItem, - onOptionClick: (RoomPrivacyItem) -> Unit, +fun RoomVisibilityOption( + roomPrivacyItem: RoomVisibilityItem, + onOptionClick: (RoomVisibilityItem) -> Unit, modifier: Modifier = Modifier, isSelected: Boolean = false, ) { @@ -44,28 +48,31 @@ fun RoomPrivacyOption( onClick = { onOptionClick(roomPrivacyItem) }, role = Role.RadioButton, ) - .padding(8.dp), ) { - Icon( - modifier = Modifier.padding(horizontal = 8.dp), - resourceId = roomPrivacyItem.icon, - contentDescription = null, - tint = MaterialTheme.colorScheme.secondary, - ) - - Column( - Modifier - .weight(1f) - .padding(horizontal = 8.dp) + Box( + modifier = modifier + .size(30.dp) + .clip(RoundedCornerShape(8.dp)) + .background(ElementTheme.colors.bgSubtleSecondary) + .padding(3.dp), + contentAlignment = Alignment.Center, ) { + Icon( + resourceId = roomPrivacyItem.icon, + contentDescription = null, + tint = if(isSelected) ElementTheme.colors.iconPrimary else ElementTheme.colors.iconSecondary, + ) + } + Spacer(Modifier.size(16.dp)) + Column(Modifier.weight(1f)) { Text( - text = roomPrivacyItem.title, + text = stringResource(roomPrivacyItem.title), style = ElementTheme.typography.fontBodyLgRegular, color = MaterialTheme.colorScheme.primary, ) Spacer(Modifier.size(3.dp)) Text( - text = roomPrivacyItem.description, + text = stringResource(roomPrivacyItem.description), style = ElementTheme.typography.fontBodySmRegular, color = MaterialTheme.colorScheme.tertiary, ) @@ -85,14 +92,14 @@ fun RoomPrivacyOption( @PreviewsDayNight @Composable internal fun RoomPrivacyOptionPreview() = ElementPreview { - val aRoomPrivacyItem = roomPrivacyItems().first() + val aRoomPrivacyItem = RoomVisibilityItem.Private Column { - RoomPrivacyOption( + RoomVisibilityOption( roomPrivacyItem = aRoomPrivacyItem, onOptionClick = {}, isSelected = true, ) - RoomPrivacyOption( + RoomVisibilityOption( roomPrivacyItem = aRoomPrivacyItem, onOptionClick = {}, isSelected = false, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt index d6f647fe61..048cd1cfe0 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt @@ -14,8 +14,10 @@ import io.element.android.libraries.matrix.ui.media.AvatarAction sealed interface ConfigureRoomEvents { data class RoomNameChanged(val name: String) : ConfigureRoomEvents data class TopicChanged(val topic: String) : ConfigureRoomEvents - data class RoomPrivacyChanged(val privacy: RoomPrivacy) : ConfigureRoomEvents - data class RemoveFromSelection(val matrixUser: MatrixUser) : ConfigureRoomEvents + data class RoomVisibilityChanged(val visibilityItem: RoomVisibilityItem) : ConfigureRoomEvents + data class RoomAccessChanged(val roomAccess: RoomAccessItem) : ConfigureRoomEvents + data class RoomAddressChanged(val roomAddress: String) : ConfigureRoomEvents + data class RemoveUserFromSelection(val matrixUser: MatrixUser) : ConfigureRoomEvents data class CreateRoom(val config: CreateRoomConfig) : ConfigureRoomEvents data class HandleAvatarAction(val action: AvatarAction) : ConfigureRoomEvents data object CancelCreateRoom : ConfigureRoomEvents diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index 09553d6160..a07bb57c84 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -54,7 +54,7 @@ class ConfigureRoomPresenter @Inject constructor( @Composable override fun present(): ConfigureRoomState { val cameraPermissionState = cameraPermissionPresenter.present() - val createRoomConfig = dataStore.getCreateRoomConfig().collectAsState(CreateRoomConfig()) + val createRoomConfig = dataStore.createRoomConfig.collectAsState(CreateRoomConfig()) val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker( onResult = { uri -> if (uri != null) dataStore.setAvatarUri(uri = uri, cached = true) }, @@ -92,8 +92,10 @@ class ConfigureRoomPresenter @Inject constructor( when (event) { is ConfigureRoomEvents.RoomNameChanged -> dataStore.setRoomName(event.name) is ConfigureRoomEvents.TopicChanged -> dataStore.setTopic(event.topic) - is ConfigureRoomEvents.RoomPrivacyChanged -> dataStore.setPrivacy(event.privacy) - is ConfigureRoomEvents.RemoveFromSelection -> dataStore.selectedUserListDataStore.removeUserFromSelection(event.matrixUser) + is ConfigureRoomEvents.RoomVisibilityChanged -> dataStore.setRoomVisibility(event.visibilityItem) + is ConfigureRoomEvents.RemoveUserFromSelection -> dataStore.selectedUserListDataStore.removeUserFromSelection(event.matrixUser) + is ConfigureRoomEvents.RoomAccessChanged -> dataStore.setRoomAccess(event.roomAccess) + is ConfigureRoomEvents.RoomAddressChanged -> dataStore.setRoomAddress(event.roomAddress) is ConfigureRoomEvents.CreateRoom -> createRoom(event.config) is ConfigureRoomEvents.HandleAvatarAction -> { when (event.action) { @@ -109,6 +111,7 @@ class ConfigureRoomPresenter @Inject constructor( } ConfigureRoomEvents.CancelCreateRoom -> createRoomAction.value = AsyncAction.Uninitialized + } } @@ -130,10 +133,10 @@ class ConfigureRoomPresenter @Inject constructor( val params = CreateRoomParameters( name = config.roomName, topic = config.topic, - isEncrypted = config.privacy == RoomPrivacy.Private, + isEncrypted = config.roomVisibility is RoomVisibilityState.Private, isDirect = false, - visibility = if (config.privacy == RoomPrivacy.Public) RoomVisibility.PUBLIC else RoomVisibility.PRIVATE, - preset = if (config.privacy == RoomPrivacy.Public) RoomPreset.PUBLIC_CHAT else RoomPreset.PRIVATE_CHAT, + visibility = if (config.roomVisibility is RoomVisibilityState.Public) RoomVisibility.PUBLIC else RoomVisibility.PRIVATE, + preset = if (config.roomVisibility is RoomVisibilityState.Public) RoomPreset.PUBLIC_CHAT else RoomPreset.PRIVATE_CHAT, invite = config.invites.map { it.userId }, avatar = avatarUrl, ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt index ba49ffcbdd..e89ec0e927 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt @@ -24,7 +24,10 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider RoomVisibilityItem.Private + is RoomVisibilityState.Public -> RoomVisibilityItem.Public + }, onOptionClick = { focusManager.clearFocus() - state.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(it.privacy)) + state.eventSink(ConfigureRoomEvents.RoomVisibilityChanged(it)) }, ) + if (state.config.roomVisibility is RoomVisibilityState.Public) { + RoomAccessOptions( + selected = state.config.roomVisibility.roomAccess, + onOptionClick = { + focusManager.clearFocus() + state.eventSink(ConfigureRoomEvents.RoomAccessChanged(it)) + }, + ) + RoomAddress( + modifier = Modifier.padding(horizontal = 16.dp), + address = state.config.roomVisibility.roomAddress, + onAddressChange = { state.eventSink(ConfigureRoomEvents.RoomAddressChanged(it)) }, + ) + } } } @@ -221,24 +246,125 @@ private fun RoomTopic( } @Composable -private fun RoomPrivacyOptions( - selected: RoomPrivacy?, - onOptionClick: (RoomPrivacyItem) -> Unit, +private fun ConfigureRoomOptions( + title: String, + verticalArrangement: Arrangement.Vertical, + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit, +) { + Column( + modifier = modifier + .selectableGroup() + .padding(horizontal = 12.dp), + verticalArrangement = verticalArrangement, + ) { + Text( + text = title, + style = ElementTheme.typography.fontBodyLgMedium, + color = ElementTheme.colors.textPrimary, + ) + content() + } +} + +@Composable +private fun RoomVisibilityOptions( + selected: RoomVisibilityItem, + onOptionClick: (RoomVisibilityItem) -> Unit, modifier: Modifier = Modifier, ) { - val items = roomPrivacyItems() - Column(modifier = modifier.selectableGroup()) { - items.forEach { item -> - RoomPrivacyOption( + ConfigureRoomOptions( + title = "Room visibility", + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + RoomVisibilityItem.entries.forEach { item -> + RoomVisibilityOption( roomPrivacyItem = item, - isSelected = selected == item.privacy, + isSelected = item == selected, onOptionClick = onOptionClick, ) } } } +@Composable +private fun RoomAccessOptions( + selected: RoomAccess, + onOptionClick: (RoomAccessItem) -> Unit, + modifier: Modifier = Modifier, +) { + ConfigureRoomOptions( + title = "Room access", + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + RoomAccessItem.entries.forEach { item -> + RoomAccessOption( + roomAccessItem = item, + isSelected = when (item) { + RoomAccessItem.Anyone -> selected == RoomAccess.Anyone + RoomAccessItem.AskToJoin -> selected == RoomAccess.Knocking + }, + onOptionClick = onOptionClick, + ) + } + } +} + +@Composable +private fun RoomAddress( + address: RoomAddress, + onAddressChange: (String) -> Unit, + modifier: Modifier = Modifier, +){ + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + modifier = Modifier.padding(horizontal = 16.dp), + style = ElementTheme.typography.fontBodyMdRegular, + color = MaterialTheme.colorScheme.primary, + text = "Room address", + ) + + TextField( + modifier = Modifier.fillMaxWidth(), + value = when(address) { + is RoomAddress.AutoFilled -> address.address + is RoomAddress.Edited -> address.address + }, + leadingIcon = { + Text( + text = "#", + style = ElementTheme.typography.fontBodyLgMedium, + color = ElementTheme.colors.textSecondary, + ) + }, + trailingIcon = { + Text( + text = ":myserver.com", + style = ElementTheme.typography.fontBodyLgMedium, + color = ElementTheme.colors.textSecondary, + modifier = Modifier.padding(end = 16.dp) + ) + }, + supportingText = { + Text( + text = "In order for this room to be visible in the public room directory, you will need to a room address. ", + style = ElementTheme.typography.fontBodySmRegular, + color = ElementTheme.colors.textSecondary, + ) + }, + onValueChange = onAddressChange, + singleLine = true, + ) + } +} + @PreviewsDayNight +@PreviewWithLargeHeight @Composable internal fun ConfigureRoomViewPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = ElementPreview { ConfigureRoomView( diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacy.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccess.kt similarity index 67% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacy.kt rename to features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccess.kt index d376b84681..3a86280f11 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacy.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccess.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023, 2024 New Vector Ltd. + * Copyright 2024 New Vector Ltd. * * SPDX-License-Identifier: AGPL-3.0-only * Please see LICENSE in the repository root for full details. @@ -7,7 +7,6 @@ package io.element.android.features.createroom.impl.configureroom -enum class RoomPrivacy { - Private, - Public, +enum class RoomAccess { + Anyone, Knocking, Invite } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccessItem.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccessItem.kt new file mode 100644 index 0000000000..6c21935196 --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccessItem.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.createroom.impl.configureroom + +import androidx.annotation.StringRes +import io.element.android.libraries.ui.strings.CommonStrings + +enum class RoomAccessItem( + @StringRes val title: Int, + @StringRes val description: Int +) { + Anyone( + title = CommonStrings.screen_create_room_access_section_anyone_option_title, + description = CommonStrings.screen_create_room_access_section_anyone_option_description, + ), + AskToJoin( + title = CommonStrings.screen_create_room_access_section_knocking_option_title, + description = CommonStrings.screen_create_room_access_section_knocking_option_description, + ) +} + diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddress.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddress.kt new file mode 100644 index 0000000000..a26888e08d --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddress.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.createroom.impl.configureroom + +sealed interface RoomAddress { + data class AutoFilled(val address: String) : RoomAddress + data class Edited(val address: String) : RoomAddress +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt deleted file mode 100644 index a0b7e4cc05..0000000000 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2023, 2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only - * Please see LICENSE in the repository root for full details. - */ - -package io.element.android.features.createroom.impl.configureroom - -import androidx.annotation.DrawableRes -import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource -import io.element.android.features.createroom.impl.R -import io.element.android.libraries.designsystem.icons.CompoundDrawables -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList - -data class RoomPrivacyItem( - val privacy: RoomPrivacy, - @DrawableRes val icon: Int, - val title: String, - val description: String, -) - -@Composable -fun roomPrivacyItems(): ImmutableList { - return RoomPrivacy.entries - .map { - when (it) { - RoomPrivacy.Private -> RoomPrivacyItem( - privacy = it, - icon = CompoundDrawables.ic_compound_lock_solid, - title = stringResource(R.string.screen_create_room_private_option_title), - description = stringResource(R.string.screen_create_room_private_option_description), - ) - RoomPrivacy.Public -> RoomPrivacyItem( - privacy = it, - icon = CompoundDrawables.ic_compound_public, - title = stringResource(R.string.screen_create_room_public_option_title), - description = stringResource(R.string.screen_create_room_public_option_description), - ) - } - } - .toImmutableList() -} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityItem.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityItem.kt new file mode 100644 index 0000000000..12909cdd5e --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityItem.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2023, 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.createroom.impl.configureroom + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import io.element.android.features.createroom.impl.R +import io.element.android.libraries.designsystem.icons.CompoundDrawables + +enum class RoomVisibilityItem( + @DrawableRes val icon: Int, + @StringRes val title: Int, + @StringRes val description: Int +) { + Private( + icon = CompoundDrawables.ic_compound_lock, + title = R.string.screen_create_room_private_option_title, + description = R.string.screen_create_room_private_option_description, + ), + Public( + icon = CompoundDrawables.ic_compound_public, + title = R.string.screen_create_room_public_option_title, + description = R.string.screen_create_room_public_option_description, + ) +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityState.kt new file mode 100644 index 0000000000..0a50a98a3f --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityState.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2023, 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.createroom.impl.configureroom + +sealed interface RoomVisibilityState { + val roomAccess: RoomAccess + + data object Private : RoomVisibilityState { + override val roomAccess: RoomAccess = RoomAccess.Invite + } + + data class Public( + val roomAddress: RoomAddress, + override val roomAccess: RoomAccess + ) : RoomVisibilityState +} diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTest.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTest.kt index 9294f95b78..3c09e34ce0 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTest.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTest.kt @@ -103,7 +103,7 @@ class ConfigureRoomPresenterTest { assertThat(initialState.config.topic).isNull() assertThat(initialState.config.invites).isEmpty() assertThat(initialState.config.avatarUri).isNull() - assertThat(initialState.config.privacy).isEqualTo(RoomPrivacy.Private) + assertThat(initialState.config.roomVisibility).isEqualTo(RoomVisibilityState.Private) } } @@ -200,13 +200,13 @@ class ConfigureRoomPresenterTest { assertThat(newState.config).isEqualTo(expectedConfig) // Room privacy - newState.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(RoomPrivacy.Public)) + newState.eventSink(ConfigureRoomEvents.RoomVisibilityChanged(RoomVisibilityState.Public)) newState = awaitItem() - expectedConfig = expectedConfig.copy(privacy = RoomPrivacy.Public) + expectedConfig = expectedConfig.copy(roomVisibility = RoomVisibilityState.Public) assertThat(newState.config).isEqualTo(expectedConfig) // Remove user - newState.eventSink(ConfigureRoomEvents.RemoveFromSelection(selectedUser1)) + newState.eventSink(ConfigureRoomEvents.RemoveUserFromSelection(selectedUser1)) newState = awaitItem() expectedConfig = expectedConfig.copy(invites = expectedConfig.invites.minus(selectedUser1).toImmutableList()) assertThat(newState.config).isEqualTo(expectedConfig) From dc9e883e934bc43ca51faf44d941fc34035475fb Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 25 Oct 2024 18:36:08 +0200 Subject: [PATCH 09/50] create room : remove RoomAccess.Invite as we are not displaying it. --- .../impl/configureroom/ConfigureRoomView.kt | 20 ++++++++----------- .../impl/configureroom/RoomAccess.kt | 2 +- .../impl/configureroom/RoomAccessItem.kt | 2 +- .../impl/configureroom/RoomVisibilityState.kt | 8 ++------ 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index 636230ec71..d2bf16fdc4 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -14,15 +14,12 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.selection.selectableGroup -import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api @@ -35,7 +32,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardCapitalization -import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme @@ -133,7 +129,10 @@ fun ConfigureRoomView( ) if (state.config.roomVisibility is RoomVisibilityState.Public) { RoomAccessOptions( - selected = state.config.roomVisibility.roomAccess, + selected = when (state.config.roomVisibility.roomAccess) { + RoomAccess.Anyone -> RoomAccessItem.Anyone + RoomAccess.Knocking -> RoomAccessItem.AskToJoin + }, onOptionClick = { focusManager.clearFocus() state.eventSink(ConfigureRoomEvents.RoomAccessChanged(it)) @@ -290,7 +289,7 @@ private fun RoomVisibilityOptions( @Composable private fun RoomAccessOptions( - selected: RoomAccess, + selected: RoomAccessItem, onOptionClick: (RoomAccessItem) -> Unit, modifier: Modifier = Modifier, ) { @@ -302,10 +301,7 @@ private fun RoomAccessOptions( RoomAccessItem.entries.forEach { item -> RoomAccessOption( roomAccessItem = item, - isSelected = when (item) { - RoomAccessItem.Anyone -> selected == RoomAccess.Anyone - RoomAccessItem.AskToJoin -> selected == RoomAccess.Knocking - }, + isSelected = item == selected, onOptionClick = onOptionClick, ) } @@ -317,7 +313,7 @@ private fun RoomAddress( address: RoomAddress, onAddressChange: (String) -> Unit, modifier: Modifier = Modifier, -){ +) { Column( modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp), @@ -331,7 +327,7 @@ private fun RoomAddress( TextField( modifier = Modifier.fillMaxWidth(), - value = when(address) { + value = when (address) { is RoomAddress.AutoFilled -> address.address is RoomAddress.Edited -> address.address }, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccess.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccess.kt index 3a86280f11..aeaf63b2c5 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccess.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccess.kt @@ -8,5 +8,5 @@ package io.element.android.features.createroom.impl.configureroom enum class RoomAccess { - Anyone, Knocking, Invite + Anyone, Knocking } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccessItem.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccessItem.kt index 6c21935196..c8791e1138 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccessItem.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccessItem.kt @@ -21,6 +21,6 @@ enum class RoomAccessItem( AskToJoin( title = CommonStrings.screen_create_room_access_section_knocking_option_title, description = CommonStrings.screen_create_room_access_section_knocking_option_description, - ) + ), } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityState.kt index 0a50a98a3f..ad61fadb3a 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityState.kt @@ -8,14 +8,10 @@ package io.element.android.features.createroom.impl.configureroom sealed interface RoomVisibilityState { - val roomAccess: RoomAccess - - data object Private : RoomVisibilityState { - override val roomAccess: RoomAccess = RoomAccess.Invite - } + data object Private : RoomVisibilityState data class Public( val roomAddress: RoomAddress, - override val roomAccess: RoomAccess + val roomAccess: RoomAccess ) : RoomVisibilityState } From 9e2b01e4cea0c28d850ce9cc449c12322dd32a13 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 29 Oct 2024 16:54:09 +0100 Subject: [PATCH 10/50] create room : use user homeserver in the address field. --- .../createroom/impl/configureroom/ConfigureRoomPresenter.kt | 2 ++ .../createroom/impl/configureroom/ConfigureRoomState.kt | 1 + .../impl/configureroom/ConfigureRoomStateProvider.kt | 1 + .../createroom/impl/configureroom/ConfigureRoomView.kt | 4 +++- 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index a07bb57c84..ec3e31f1e0 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -55,6 +55,7 @@ class ConfigureRoomPresenter @Inject constructor( override fun present(): ConfigureRoomState { val cameraPermissionState = cameraPermissionPresenter.present() val createRoomConfig = dataStore.createRoomConfig.collectAsState(CreateRoomConfig()) + val homeserverName = remember { matrixClient.userIdServerName() } val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker( onResult = { uri -> if (uri != null) dataStore.setAvatarUri(uri = uri, cached = true) }, @@ -120,6 +121,7 @@ class ConfigureRoomPresenter @Inject constructor( avatarActions = avatarActions, createRoomAction = createRoomAction.value, cameraPermissionState = cameraPermissionState, + homeserverName = homeserverName, eventSink = ::handleEvents, ) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt index 70a0a9b76d..b59ae887e5 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt @@ -19,6 +19,7 @@ data class ConfigureRoomState( val avatarActions: ImmutableList, val createRoomAction: AsyncAction, val cameraPermissionState: PermissionsState, + val homeserverName: String, val eventSink: (ConfigureRoomEvents) -> Unit ) { val isCreateButtonEnabled: Boolean = config.roomName.isNullOrEmpty().not() diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt index e89ec0e927..af32ac8b13 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt @@ -38,5 +38,6 @@ fun aConfigureRoomState() = ConfigureRoomState( avatarActions = persistentListOf(), createRoomAction = AsyncAction.Uninitialized, cameraPermissionState = aPermissionsState(showDialog = false), + homeserverName = "matrix.org", eventSink = { }, ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index d2bf16fdc4..74521197b5 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -141,6 +141,7 @@ fun ConfigureRoomView( RoomAddress( modifier = Modifier.padding(horizontal = 16.dp), address = state.config.roomVisibility.roomAddress, + homeserverName = state.homeserverName, onAddressChange = { state.eventSink(ConfigureRoomEvents.RoomAddressChanged(it)) }, ) } @@ -311,6 +312,7 @@ private fun RoomAccessOptions( @Composable private fun RoomAddress( address: RoomAddress, + homeserverName: String, onAddressChange: (String) -> Unit, modifier: Modifier = Modifier, ) { @@ -340,7 +342,7 @@ private fun RoomAddress( }, trailingIcon = { Text( - text = ":myserver.com", + text = homeserverName, style = ElementTheme.typography.fontBodyLgMedium, color = ElementTheme.colors.textSecondary, modifier = Modifier.padding(end = 16.dp) From f0c1dfa768d6319de251abcceeb8c87f25bb46b0 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 29 Oct 2024 18:10:55 +0100 Subject: [PATCH 11/50] create room : branch the join rule override --- .../configureroom/ConfigureRoomPresenter.kt | 3 +++ .../impl/configureroom/RoomAccess.kt | 9 ++++++++ .../api/createroom/CreateRoomParameters.kt | 1 + .../matrix/api/createroom/JoinRuleOverride.kt | 16 ++++++++++++++ .../libraries/matrix/impl/RustMatrixClient.kt | 21 +++++++++++++++---- 5 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/JoinRuleOverride.kt diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index ec3e31f1e0..13a4952308 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -27,6 +27,7 @@ import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters +import io.element.android.libraries.matrix.api.createroom.JoinRuleOverride import io.element.android.libraries.matrix.api.createroom.RoomPreset import io.element.android.libraries.matrix.api.createroom.RoomVisibility import io.element.android.libraries.matrix.ui.media.AvatarAction @@ -38,6 +39,7 @@ import io.element.android.services.analytics.api.AnalyticsService import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import java.util.Optional import javax.inject.Inject class ConfigureRoomPresenter @Inject constructor( @@ -138,6 +140,7 @@ class ConfigureRoomPresenter @Inject constructor( isEncrypted = config.roomVisibility is RoomVisibilityState.Private, isDirect = false, visibility = if (config.roomVisibility is RoomVisibilityState.Public) RoomVisibility.PUBLIC else RoomVisibility.PRIVATE, + joinRuleOverride = if (config.roomVisibility is RoomVisibilityState.Public) config.roomVisibility.roomAccess.toJoinRule() else JoinRuleOverride.None, preset = if (config.roomVisibility is RoomVisibilityState.Public) RoomPreset.PUBLIC_CHAT else RoomPreset.PRIVATE_CHAT, invite = config.invites.map { it.userId }, avatar = avatarUrl, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccess.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccess.kt index aeaf63b2c5..b966d98db6 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccess.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccess.kt @@ -7,6 +7,15 @@ package io.element.android.features.createroom.impl.configureroom +import io.element.android.libraries.matrix.api.createroom.JoinRuleOverride + enum class RoomAccess { Anyone, Knocking } + +fun RoomAccess.toJoinRule(): JoinRuleOverride { + return when (this) { + RoomAccess.Anyone -> JoinRuleOverride.None + RoomAccess.Knocking -> JoinRuleOverride.Knock + } +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/CreateRoomParameters.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/CreateRoomParameters.kt index 940fef7326..4c74153a31 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/CreateRoomParameters.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/CreateRoomParameters.kt @@ -18,4 +18,5 @@ data class CreateRoomParameters( val preset: RoomPreset, val invite: List? = null, val avatar: String? = null, + val joinRuleOverride: JoinRuleOverride = JoinRuleOverride.None, ) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/JoinRuleOverride.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/JoinRuleOverride.kt new file mode 100644 index 0000000000..f59f393c3e --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/JoinRuleOverride.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.matrix.api.createroom + +/** + * Rules to override the default room join rules. + */ +sealed interface JoinRuleOverride { + data object Knock : JoinRuleOverride + data object None : JoinRuleOverride +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 101d049659..c73b3c50a6 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters +import io.element.android.libraries.matrix.api.createroom.JoinRuleOverride import io.element.android.libraries.matrix.api.createroom.RoomPreset import io.element.android.libraries.matrix.api.createroom.RoomVisibility import io.element.android.libraries.matrix.api.encryption.EncryptionService @@ -108,6 +109,7 @@ import kotlin.jvm.optionals.getOrNull import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds import org.matrix.rustcomponents.sdk.CreateRoomParameters as RustCreateRoomParameters +import org.matrix.rustcomponents.sdk.JoinRule as RustJoinRule import org.matrix.rustcomponents.sdk.RoomPreset as RustRoomPreset import org.matrix.rustcomponents.sdk.RoomVisibility as RustRoomVisibility import org.matrix.rustcomponents.sdk.SyncService as ClientSyncService @@ -304,14 +306,25 @@ class RustMatrixClient( RoomVisibility.PUBLIC -> RustRoomVisibility.PUBLIC RoomVisibility.PRIVATE -> RustRoomVisibility.PRIVATE }, - preset = when (createRoomParams.preset) { - RoomPreset.PRIVATE_CHAT -> RustRoomPreset.PRIVATE_CHAT - RoomPreset.PUBLIC_CHAT -> RustRoomPreset.PUBLIC_CHAT - RoomPreset.TRUSTED_PRIVATE_CHAT -> RustRoomPreset.TRUSTED_PRIVATE_CHAT + preset = when (createRoomParams.visibility) { + RoomVisibility.PRIVATE -> { + if (createRoomParams.isDirect) { + RustRoomPreset.TRUSTED_PRIVATE_CHAT + } else { + RustRoomPreset.PRIVATE_CHAT + } + } + RoomVisibility.PUBLIC -> { + RustRoomPreset.PUBLIC_CHAT + } }, invite = createRoomParams.invite?.map { it.value }, avatar = createRoomParams.avatar, powerLevelContentOverride = defaultRoomCreationPowerLevels, + joinRuleOverride = when (createRoomParams.joinRuleOverride) { + JoinRuleOverride.Knock -> RustJoinRule.Knock + JoinRuleOverride.None -> null + } ) val roomId = RoomId(client.createRoom(rustParams)) // Wait to receive the room back from the sync but do not returns failure if it fails. From a4be2106d06447ef786ef7fec9c4b041b089a883 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 30 Oct 2024 18:20:57 +0100 Subject: [PATCH 12/50] create room : start integrating the address field --- .../createroom/impl/CreateRoomConfig.kt | 4 +++- .../createroom/impl/CreateRoomDataStore.kt | 3 ++- .../configureroom/ConfigureRoomPresenter.kt | 16 ++++++++++------ .../impl/configureroom/ConfigureRoomState.kt | 4 +--- .../ConfigureRoomStateProvider.kt | 3 ++- .../impl/configureroom/ConfigureRoomView.kt | 13 +++++-------- .../impl/configureroom/RoomAddress.kt | 6 +++--- .../configureroom/RoomAddressErrorState.kt | 17 +++++++++++++++++ .../impl/configureroom/RoomVisibilityState.kt | 19 ++++++++++++++++++- .../api/createroom/CreateRoomParameters.kt | 2 ++ .../libraries/matrix/impl/RustMatrixClient.kt | 3 ++- 11 files changed, 65 insertions(+), 25 deletions(-) create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddressErrorState.kt diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomConfig.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomConfig.kt index a83673886d..ab6e116598 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomConfig.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomConfig.kt @@ -19,4 +19,6 @@ data class CreateRoomConfig( val avatarUri: Uri? = null, val invites: ImmutableList = persistentListOf(), val roomVisibility: RoomVisibilityState = RoomVisibilityState.Private, -) +) { + val isValid = roomName.isNullOrEmpty().not() && roomVisibility.isValid() +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt index 17d7d5cc3d..1c08068d67 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt @@ -11,6 +11,7 @@ import android.net.Uri import io.element.android.features.createroom.impl.configureroom.RoomAccess import io.element.android.features.createroom.impl.configureroom.RoomAccessItem import io.element.android.features.createroom.impl.configureroom.RoomAddress +import io.element.android.features.createroom.impl.configureroom.RoomAddressErrorState import io.element.android.features.createroom.impl.configureroom.RoomVisibilityItem import io.element.android.features.createroom.impl.configureroom.RoomVisibilityState import io.element.android.features.createroom.impl.di.CreateRoomScope @@ -23,7 +24,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.getAndUpdate import java.io.File -import java.text.Normalizer import javax.inject.Inject @SingleIn(CreateRoomScope::class) @@ -86,6 +86,7 @@ class CreateRoomDataStore @Inject constructor( RoomVisibilityItem.Private -> RoomVisibilityState.Private RoomVisibilityItem.Public -> RoomVisibilityState.Public( roomAddress = RoomAddress.AutoFilled(config.roomName.orEmpty()), + roomAddressErrorState = RoomAddressErrorState.None, roomAccess = RoomAccess.Anyone, ) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index 13a4952308..7addc8aca9 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -22,7 +22,7 @@ import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.architecture.runCatchingUpdatingState +import io.element.android.libraries.architecture.runUpdatingState import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId @@ -39,7 +39,7 @@ import io.element.android.services.analytics.api.AnalyticsService import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -import java.util.Optional +import timber.log.Timber import javax.inject.Inject class ConfigureRoomPresenter @Inject constructor( @@ -132,7 +132,7 @@ class ConfigureRoomPresenter @Inject constructor( config: CreateRoomConfig, createRoomAction: MutableState> ) = launch { - suspend { + runUpdatingState(createRoomAction) { val avatarUrl = config.avatarUri?.let { uploadAvatar(it) } val params = CreateRoomParameters( name = config.roomName, @@ -144,13 +144,17 @@ class ConfigureRoomPresenter @Inject constructor( preset = if (config.roomVisibility is RoomVisibilityState.Public) RoomPreset.PUBLIC_CHAT else RoomPreset.PRIVATE_CHAT, invite = config.invites.map { it.userId }, avatar = avatarUrl, + canonicalAlias = config.roomVisibility.roomAddress() ) - matrixClient.createRoom(params).getOrThrow() - .also { + matrixClient.createRoom(params) + .onFailure { failure -> + Timber.e(failure, "Failed to create room") + } + .onSuccess { dataStore.clearCachedData() analyticsService.capture(CreatedRoom(isDM = false)) } - }.runCatchingUpdatingState(createRoomAction) + } } private suspend fun uploadAvatar(avatarUri: Uri): String { diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt index b59ae887e5..2bbcd521ac 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt @@ -21,6 +21,4 @@ data class ConfigureRoomState( val cameraPermissionState: PermissionsState, val homeserverName: String, val eventSink: (ConfigureRoomEvents) -> Unit -) { - val isCreateButtonEnabled: Boolean = config.roomName.isNullOrEmpty().not() -} +) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt index af32ac8b13..42ace6c73a 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt @@ -26,7 +26,8 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider Unit, @@ -326,13 +326,10 @@ private fun RoomAddress( color = MaterialTheme.colorScheme.primary, text = "Room address", ) - + TextField( modifier = Modifier.fillMaxWidth(), - value = when (address) { - is RoomAddress.AutoFilled -> address.address - is RoomAddress.Edited -> address.address - }, + value = address.value, leadingIcon = { Text( text = "#", diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddress.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddress.kt index a26888e08d..5c10cfb7b5 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddress.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddress.kt @@ -7,7 +7,7 @@ package io.element.android.features.createroom.impl.configureroom -sealed interface RoomAddress { - data class AutoFilled(val address: String) : RoomAddress - data class Edited(val address: String) : RoomAddress +sealed class RoomAddress(open val value: String) { + data class AutoFilled(override val value: String) : RoomAddress(value) + data class Edited(override val value: String) : RoomAddress(value) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddressErrorState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddressErrorState.kt new file mode 100644 index 0000000000..f2cadfd6bb --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAddressErrorState.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.createroom.impl.configureroom + +/** + * Represents the error state of a room address. + */ +sealed interface RoomAddressErrorState { + data object InvalidCharacters : RoomAddressErrorState + data object AlreadyExists : RoomAddressErrorState + data object None : RoomAddressErrorState +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityState.kt index ad61fadb3a..cc5af7836e 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomVisibilityState.kt @@ -7,11 +7,28 @@ package io.element.android.features.createroom.impl.configureroom +import java.util.Optional + sealed interface RoomVisibilityState { data object Private : RoomVisibilityState data class Public( val roomAddress: RoomAddress, - val roomAccess: RoomAccess + val roomAddressErrorState: RoomAddressErrorState, + val roomAccess: RoomAccess, ) : RoomVisibilityState + + fun roomAddress(): Optional { + return when (this) { + is Private -> Optional.empty() + is Public -> Optional.of(roomAddress.value) + } + } + + fun isValid(): Boolean { + return when (this) { + is Private -> true + is Public -> roomAddressErrorState is RoomAddressErrorState.None && roomAddress.value.isNotEmpty() + } + } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/CreateRoomParameters.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/CreateRoomParameters.kt index 4c74153a31..2e13623c96 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/CreateRoomParameters.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/CreateRoomParameters.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.matrix.api.createroom import io.element.android.libraries.matrix.api.core.UserId +import java.util.Optional data class CreateRoomParameters( val name: String?, @@ -19,4 +20,5 @@ data class CreateRoomParameters( val invite: List? = null, val avatar: String? = null, val joinRuleOverride: JoinRuleOverride = JoinRuleOverride.None, + val canonicalAlias: Optional = Optional.empty(), ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index c73b3c50a6..3417c9b377 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -324,7 +324,8 @@ class RustMatrixClient( joinRuleOverride = when (createRoomParams.joinRuleOverride) { JoinRuleOverride.Knock -> RustJoinRule.Knock JoinRuleOverride.None -> null - } + }, + canonicalAlias = createRoomParams.canonicalAlias.getOrNull(), ) val roomId = RoomId(client.createRoom(rustParams)) // Wait to receive the room back from the sync but do not returns failure if it fails. From 68bdc313210bbed55c0b43cb4aca4e6b881011ce Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 31 Oct 2024 17:11:58 +0100 Subject: [PATCH 13/50] create room : add Knock feature flag --- features/createroom/impl/build.gradle.kts | 2 + .../configureroom/ConfigureRoomPresenter.kt | 5 +++ .../impl/configureroom/ConfigureRoomState.kt | 1 + .../ConfigureRoomStateProvider.kt | 41 +++++++++++++++---- .../impl/configureroom/ConfigureRoomView.kt | 2 +- .../libraries/featureflag/api/FeatureFlags.kt | 9 +++- 6 files changed, 50 insertions(+), 10 deletions(-) diff --git a/features/createroom/impl/build.gradle.kts b/features/createroom/impl/build.gradle.kts index bcd3799c92..6ddae68a72 100644 --- a/features/createroom/impl/build.gradle.kts +++ b/features/createroom/impl/build.gradle.kts @@ -40,6 +40,7 @@ dependencies { implementation(projects.libraries.usersearch.impl) implementation(projects.services.analytics.api) implementation(libs.coil.compose) + implementation(projects.libraries.featureflag.api) api(projects.features.createroom.api) testImplementation(libs.test.junit) @@ -56,6 +57,7 @@ dependencies { testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.usersearch.test) testImplementation(projects.features.createroom.test) + testImplementation(projects.libraries.featureflag.test) testImplementation(projects.tests.testutils) testImplementation(libs.androidx.compose.ui.test.junit) testReleaseImplementation(libs.androidx.compose.ui.test.manifest) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index 7addc8aca9..41a9dc8f97 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -24,6 +24,8 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runUpdatingState import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters @@ -49,6 +51,7 @@ class ConfigureRoomPresenter @Inject constructor( private val mediaPreProcessor: MediaPreProcessor, private val analyticsService: AnalyticsService, permissionsPresenterFactory: PermissionsPresenter.Factory, + private val featureFlagService: FeatureFlagService, ) : Presenter { private val cameraPermissionPresenter: PermissionsPresenter = permissionsPresenterFactory.create(android.Manifest.permission.CAMERA) private var pendingPermissionRequest = false @@ -58,6 +61,7 @@ class ConfigureRoomPresenter @Inject constructor( val cameraPermissionState = cameraPermissionPresenter.present() val createRoomConfig = dataStore.createRoomConfig.collectAsState(CreateRoomConfig()) val homeserverName = remember { matrixClient.userIdServerName() } + val isKnockFeatureEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock).collectAsState(initial = false) val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker( onResult = { uri -> if (uri != null) dataStore.setAvatarUri(uri = uri, cached = true) }, @@ -119,6 +123,7 @@ class ConfigureRoomPresenter @Inject constructor( } return ConfigureRoomState( + isKnockFeatureEnabled = isKnockFeatureEnabled, config = createRoomConfig.value, avatarActions = avatarActions, createRoomAction = createRoomAction.value, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt index 2bbcd521ac..8a2122c306 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt @@ -15,6 +15,7 @@ import io.element.android.libraries.permissions.api.PermissionsState import kotlinx.collections.immutable.ImmutableList data class ConfigureRoomState( + val isKnockFeatureEnabled: Boolean, val config: CreateRoomConfig, val avatarActions: ImmutableList, val createRoomAction: AsyncAction, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt index 42ace6c73a..5b1427cd47 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt @@ -10,7 +10,10 @@ package io.element.android.features.createroom.impl.configureroom import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.ui.components.aMatrixUserList +import io.element.android.libraries.matrix.ui.media.AvatarAction +import io.element.android.libraries.permissions.api.PermissionsState import io.element.android.libraries.permissions.api.aPermissionsState import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -19,7 +22,20 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider get() = sequenceOf( aConfigureRoomState(), - aConfigureRoomState().copy( + aConfigureRoomState( + config = CreateRoomConfig( + roomName = "Room 101", + topic = "Room topic for this room when the text goes onto multiple lines and is really long, there shouldn’t be more than 3 lines", + invites = aMatrixUserList().toImmutableList(), + roomVisibility = RoomVisibilityState.Public( + roomAddress = RoomAddress.AutoFilled("Room 101"), + roomAccess = RoomAccess.Knocking, + roomAddressErrorState = RoomAddressErrorState.None, + ), + ), + ), + aConfigureRoomState( + isKnockFeatureEnabled = false, config = CreateRoomConfig( roomName = "Room 101", topic = "Room topic for this room when the text goes onto multiple lines and is really long, there shouldn’t be more than 3 lines", @@ -34,11 +50,20 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider = emptyList(), + createRoomAction: AsyncAction = AsyncAction.Uninitialized, + cameraPermissionState: PermissionsState = aPermissionsState(showDialog = false), + homeserverName: String = "matrix.org", + eventSink: (ConfigureRoomEvents) -> Unit = { }, +) = ConfigureRoomState( + config = config, + isKnockFeatureEnabled = isKnockFeatureEnabled, + avatarActions = avatarActions.toImmutableList(), + createRoomAction = createRoomAction, + cameraPermissionState = cameraPermissionState, + homeserverName = homeserverName, + eventSink = eventSink, ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index 13c1179fe0..cfce124019 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -127,7 +127,7 @@ fun ConfigureRoomView( state.eventSink(ConfigureRoomEvents.RoomVisibilityChanged(it)) }, ) - if (state.config.roomVisibility is RoomVisibilityState.Public) { + if (state.config.roomVisibility is RoomVisibilityState.Public && state.isKnockFeatureEnabled) { RoomAccessOptions( selected = when (state.config.roomVisibility.roomAccess) { RoomAccess.Anyone -> RoomAccessItem.Anyone diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt index 1a8b8935b4..de7fe02b5d 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt @@ -124,5 +124,12 @@ enum class FeatureFlags( " You'll have to stop and re-open the app manually for that setting to take effect.", defaultValue = { false }, isFinished = false, - ) + ), + Knock( + key = "feature.knock", + title = "Ask to join", + description = "Allow creating rooms which users can request access to.", + defaultValue = { false }, + isFinished = false, + ), } From 1b1884c3e8be1d20eb418ef208fd78cab9f42f32 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 4 Nov 2024 15:35:33 +0100 Subject: [PATCH 14/50] create room : fix tests and remove auto-fill alias for now. --- .../createroom/impl/CreateRoomDataStore.kt | 3 +- .../impl/components/RoomAccessOption.kt | 19 +- .../impl/components/RoomVisibilityOption.kt | 4 +- .../impl/configureroom/ConfigureRoomEvents.kt | 3 +- .../configureroom/ConfigureRoomPresenter.kt | 50 +++-- .../ConfigureRoomStateProvider.kt | 3 +- .../impl/configureroom/ConfigureRoomView.kt | 8 +- .../impl/configureroom/RoomAccess.kt | 3 +- .../impl/configureroom/RoomAccessItem.kt | 1 - .../ConfigureRoomPresenterTest.kt | 198 +++++++++++------- 10 files changed, 169 insertions(+), 123 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt index 1c08068d67..36feb0aec3 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt @@ -46,6 +46,7 @@ class CreateRoomDataStore @Inject constructor( fun setRoomName(roomName: String) { createRoomConfigFlow.getAndUpdate { config -> + /* val newVisibility = when (config.roomVisibility) { is RoomVisibilityState.Public -> { val roomAddress = config.roomVisibility.roomAddress @@ -59,9 +60,9 @@ class CreateRoomDataStore @Inject constructor( } else -> config.roomVisibility } + */ config.copy( roomName = roomName.takeIf { it.isNotEmpty() }, - roomVisibility = newVisibility, ) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomAccessOption.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomAccessOption.kt index 4d39f698be..6cde477f2e 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomAccessOption.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomAccessOption.kt @@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.selection.selectable import androidx.compose.material3.MaterialTheme @@ -36,13 +35,13 @@ fun RoomAccessOption( isSelected: Boolean = false, ) { Row( - modifier - .fillMaxWidth() - .selectable( - selected = isSelected, - onClick = { onOptionClick(roomAccessItem) }, - role = Role.RadioButton, - ) + modifier + .fillMaxWidth() + .selectable( + selected = isSelected, + onClick = { onOptionClick(roomAccessItem) }, + role = Role.RadioButton, + ) ) { Column(Modifier.weight(1f)) { Text( @@ -59,8 +58,8 @@ fun RoomAccessOption( } RadioButton( modifier = Modifier - .align(Alignment.CenterVertically) - .size(48.dp), + .align(Alignment.CenterVertically) + .size(48.dp), selected = isSelected, // null recommended for accessibility with screenreaders onClick = null diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt index cf6ab077a4..15ae5c11a1 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt @@ -50,7 +50,7 @@ fun RoomVisibilityOption( ) ) { Box( - modifier = modifier + modifier = Modifier .size(30.dp) .clip(RoundedCornerShape(8.dp)) .background(ElementTheme.colors.bgSubtleSecondary) @@ -60,7 +60,7 @@ fun RoomVisibilityOption( Icon( resourceId = roomPrivacyItem.icon, contentDescription = null, - tint = if(isSelected) ElementTheme.colors.iconPrimary else ElementTheme.colors.iconSecondary, + tint = if (isSelected) ElementTheme.colors.iconPrimary else ElementTheme.colors.iconSecondary, ) } Spacer(Modifier.size(16.dp)) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt index 048cd1cfe0..26fee055ee 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt @@ -7,7 +7,6 @@ package io.element.android.features.createroom.impl.configureroom -import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.media.AvatarAction @@ -18,7 +17,7 @@ sealed interface ConfigureRoomEvents { data class RoomAccessChanged(val roomAccess: RoomAccessItem) : ConfigureRoomEvents data class RoomAddressChanged(val roomAddress: String) : ConfigureRoomEvents data class RemoveUserFromSelection(val matrixUser: MatrixUser) : ConfigureRoomEvents - data class CreateRoom(val config: CreateRoomConfig) : ConfigureRoomEvents + data object CreateRoom : ConfigureRoomEvents data class HandleAvatarAction(val action: AvatarAction) : ConfigureRoomEvents data object CancelCreateRoom : ConfigureRoomEvents } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index 41a9dc8f97..1b7623adfb 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -22,14 +22,13 @@ import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.architecture.runUpdatingState +import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters -import io.element.android.libraries.matrix.api.createroom.JoinRuleOverride import io.element.android.libraries.matrix.api.createroom.RoomPreset import io.element.android.libraries.matrix.api.createroom.RoomVisibility import io.element.android.libraries.matrix.ui.media.AvatarAction @@ -61,7 +60,7 @@ class ConfigureRoomPresenter @Inject constructor( val cameraPermissionState = cameraPermissionPresenter.present() val createRoomConfig = dataStore.createRoomConfig.collectAsState(CreateRoomConfig()) val homeserverName = remember { matrixClient.userIdServerName() } - val isKnockFeatureEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock).collectAsState(initial = false) + val isKnockFeatureEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock).collectAsState(initial = false) val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker( onResult = { uri -> if (uri != null) dataStore.setAvatarUri(uri = uri, cached = true) }, @@ -103,7 +102,7 @@ class ConfigureRoomPresenter @Inject constructor( is ConfigureRoomEvents.RemoveUserFromSelection -> dataStore.selectedUserListDataStore.removeUserFromSelection(event.matrixUser) is ConfigureRoomEvents.RoomAccessChanged -> dataStore.setRoomAccess(event.roomAccess) is ConfigureRoomEvents.RoomAddressChanged -> dataStore.setRoomAddress(event.roomAddress) - is ConfigureRoomEvents.CreateRoom -> createRoom(event.config) + is ConfigureRoomEvents.CreateRoom -> createRoom(createRoomConfig.value) is ConfigureRoomEvents.HandleAvatarAction -> { when (event.action) { AvatarAction.ChoosePhoto -> galleryImagePicker.launch() @@ -118,7 +117,6 @@ class ConfigureRoomPresenter @Inject constructor( } ConfigureRoomEvents.CancelCreateRoom -> createRoomAction.value = AsyncAction.Uninitialized - } } @@ -137,20 +135,33 @@ class ConfigureRoomPresenter @Inject constructor( config: CreateRoomConfig, createRoomAction: MutableState> ) = launch { - runUpdatingState(createRoomAction) { + suspend { val avatarUrl = config.avatarUri?.let { uploadAvatar(it) } - val params = CreateRoomParameters( - name = config.roomName, - topic = config.topic, - isEncrypted = config.roomVisibility is RoomVisibilityState.Private, - isDirect = false, - visibility = if (config.roomVisibility is RoomVisibilityState.Public) RoomVisibility.PUBLIC else RoomVisibility.PRIVATE, - joinRuleOverride = if (config.roomVisibility is RoomVisibilityState.Public) config.roomVisibility.roomAccess.toJoinRule() else JoinRuleOverride.None, - preset = if (config.roomVisibility is RoomVisibilityState.Public) RoomPreset.PUBLIC_CHAT else RoomPreset.PRIVATE_CHAT, - invite = config.invites.map { it.userId }, - avatar = avatarUrl, - canonicalAlias = config.roomVisibility.roomAddress() - ) + val params = if (config.roomVisibility is RoomVisibilityState.Public) { + CreateRoomParameters( + name = config.roomName, + topic = config.topic, + isEncrypted = false, + isDirect = false, + visibility = RoomVisibility.PUBLIC, + joinRuleOverride = config.roomVisibility.roomAccess.toJoinRule(), + preset = RoomPreset.PUBLIC_CHAT, + invite = config.invites.map { it.userId }, + avatar = avatarUrl, + canonicalAlias = config.roomVisibility.roomAddress() + ) + } else { + CreateRoomParameters( + name = config.roomName, + topic = config.topic, + isEncrypted = config.roomVisibility is RoomVisibilityState.Private, + isDirect = false, + visibility = RoomVisibility.PRIVATE, + preset = RoomPreset.PRIVATE_CHAT, + invite = config.invites.map { it.userId }, + avatar = avatarUrl, + ) + } matrixClient.createRoom(params) .onFailure { failure -> Timber.e(failure, "Failed to create room") @@ -159,7 +170,8 @@ class ConfigureRoomPresenter @Inject constructor( dataStore.clearCachedData() analyticsService.capture(CreatedRoom(isDM = false)) } - } + .getOrThrow() + }.runCatchingUpdatingState(createRoomAction) } private suspend fun uploadAvatar(avatarUri: Uri): String { diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt index 5b1427cd47..80445fbff4 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt @@ -15,7 +15,6 @@ import io.element.android.libraries.matrix.ui.components.aMatrixUserList import io.element.android.libraries.matrix.ui.media.AvatarAction import io.element.android.libraries.permissions.api.PermissionsState import io.element.android.libraries.permissions.api.aPermissionsState -import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList open class ConfigureRoomStateProvider : PreviewParameterProvider { @@ -23,6 +22,7 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider().readBytes() } returns byteArrayOf() } @@ -94,50 +75,56 @@ class ConfigureRoomPresenterTest { @Test fun `present - initial state`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val presenter = createConfigureRoomPresenter() + presenter.test { + val initialState = initialState() assertThat(initialState.config).isEqualTo(CreateRoomConfig()) assertThat(initialState.config.roomName).isNull() assertThat(initialState.config.topic).isNull() assertThat(initialState.config.invites).isEmpty() assertThat(initialState.config.avatarUri).isNull() assertThat(initialState.config.roomVisibility).isEqualTo(RoomVisibilityState.Private) + assertThat(initialState.createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) + assertThat(initialState.homeserverName).isEqualTo("matrix.org") } } @Test fun `present - create room button is enabled only if the required fields are completed`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val presenter = createConfigureRoomPresenter() + presenter.test { + val initialState = initialState() var config = initialState.config - assertThat(initialState.isCreateButtonEnabled).isFalse() + assertThat(initialState.config.isValid).isFalse() // Room name not empty initialState.eventSink(ConfigureRoomEvents.RoomNameChanged(A_ROOM_NAME)) var newState: ConfigureRoomState = awaitItem() config = config.copy(roomName = A_ROOM_NAME) assertThat(newState.config).isEqualTo(config) - assertThat(newState.isCreateButtonEnabled).isTrue() + assertThat(newState.config.isValid).isTrue() // Clear room name newState.eventSink(ConfigureRoomEvents.RoomNameChanged("")) newState = awaitItem() config = config.copy(roomName = null) assertThat(newState.config).isEqualTo(config) - assertThat(newState.isCreateButtonEnabled).isFalse() + assertThat(newState.config.isValid).isFalse() } } @Test fun `present - state is updated when fields are changed`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val userListDataStore = UserListDataStore() + val pickerProvider = FakePickerProvider() + val permissionsPresenter = FakePermissionsPresenter() + val presenter = createConfigureRoomPresenter( + createRoomDataStore = CreateRoomDataStore(userListDataStore), + pickerProvider = pickerProvider, + permissionsPresenter = permissionsPresenter, + ) + presenter.test { + val initialState = initialState() var expectedConfig = CreateRoomConfig() assertThat(initialState.config).isEqualTo(expectedConfig) @@ -165,22 +152,22 @@ class ConfigureRoomPresenterTest { // Room avatar // Pick avatar - fakePickerProvider.givenResult(null) + pickerProvider.givenResult(null) // From gallery val uriFromGallery = Uri.parse(AN_URI_FROM_GALLERY) - fakePickerProvider.givenResult(uriFromGallery) + pickerProvider.givenResult(uriFromGallery) newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) newState = awaitItem() expectedConfig = expectedConfig.copy(avatarUri = uriFromGallery) assertThat(newState.config).isEqualTo(expectedConfig) // From camera val uriFromCamera = Uri.parse(AN_URI_FROM_CAMERA) - fakePickerProvider.givenResult(uriFromCamera) + pickerProvider.givenResult(uriFromCamera) assertThat(newState.cameraPermissionState.permissionGranted).isFalse() newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto)) newState = awaitItem() assertThat(newState.cameraPermissionState.showDialog).isTrue() - fakePermissionsPresenter.setPermissionGranted() + permissionsPresenter.setPermissionGranted() newState = awaitItem() assertThat(newState.cameraPermissionState.permissionGranted).isTrue() newState = awaitItem() @@ -188,7 +175,7 @@ class ConfigureRoomPresenterTest { assertThat(newState.config).isEqualTo(expectedConfig) // Do it again, no permission is requested val uriFromCamera2 = Uri.parse(AN_URI_FROM_CAMERA_2) - fakePickerProvider.givenResult(uriFromCamera2) + pickerProvider.givenResult(uriFromCamera2) newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto)) newState = awaitItem() expectedConfig = expectedConfig.copy(avatarUri = uriFromCamera2) @@ -200,9 +187,15 @@ class ConfigureRoomPresenterTest { assertThat(newState.config).isEqualTo(expectedConfig) // Room privacy - newState.eventSink(ConfigureRoomEvents.RoomVisibilityChanged(RoomVisibilityState.Public)) + newState.eventSink(ConfigureRoomEvents.RoomVisibilityChanged(RoomVisibilityItem.Public)) newState = awaitItem() - expectedConfig = expectedConfig.copy(roomVisibility = RoomVisibilityState.Public) + expectedConfig = expectedConfig.copy( + roomVisibility = RoomVisibilityState.Public( + roomAddress = RoomAddress.AutoFilled(expectedConfig.roomName ?: ""), + roomAddressErrorState = RoomAddressErrorState.None, + roomAccess = RoomAccess.Anyone, + ) + ) assertThat(newState.config).isEqualTo(expectedConfig) // Remove user @@ -215,15 +208,17 @@ class ConfigureRoomPresenterTest { @Test fun `present - trigger create room action`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val matrixClient = createMatrixClient() + val presenter = createConfigureRoomPresenter( + matrixClient = matrixClient + ) + presenter.test { + val initialState = initialState() val createRoomResult = Result.success(RoomId("!createRoomResult:domain")) - fakeMatrixClient.givenCreateRoomResult(createRoomResult) + matrixClient.givenCreateRoomResult(createRoomResult) - initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + initialState.eventSink(ConfigureRoomEvents.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterCreateRoom = awaitItem() assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Success::class.java) @@ -233,18 +228,22 @@ class ConfigureRoomPresenterTest { @Test fun `present - record analytics when creating room`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val matrixClient = createMatrixClient() + val analyticsService = FakeAnalyticsService() + val presenter = createConfigureRoomPresenter( + matrixClient = matrixClient, + analyticsService = analyticsService + ) + presenter.test { + val initialState = initialState() val createRoomResult = Result.success(RoomId("!createRoomResult:domain")) - fakeMatrixClient.givenCreateRoomResult(createRoomResult) + matrixClient.givenCreateRoomResult(createRoomResult) - initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + initialState.eventSink(ConfigureRoomEvents.CreateRoom) skipItems(2) - val analyticsEvent = fakeAnalyticsService.capturedEvents.filterIsInstance().firstOrNull() + val analyticsEvent = analyticsService.capturedEvents.filterIsInstance().firstOrNull() assertThat(analyticsEvent).isNotNull() assertThat(analyticsEvent?.isDM).isFalse() } @@ -252,23 +251,31 @@ class ConfigureRoomPresenterTest { @Test fun `present - trigger create room with upload error and retry`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - skipItems(1) + val matrixClient = createMatrixClient() + val analyticsService = FakeAnalyticsService() + val mediaPreProcessor = FakeMediaPreProcessor() + val createRoomDataStore = CreateRoomDataStore(UserListDataStore()) + val presenter = createConfigureRoomPresenter( + createRoomDataStore = createRoomDataStore, + mediaPreProcessor = mediaPreProcessor, + matrixClient = matrixClient, + analyticsService = analyticsService + ) + presenter.test { + val initialState = initialState() createRoomDataStore.setAvatarUri(Uri.parse(AN_URI_FROM_GALLERY)) - fakeMediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(mockk(), mockk(), mockk()))) - fakeMatrixClient.givenUploadMediaResult(Result.failure(A_THROWABLE)) + skipItems(1) + mediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(mockk(), mockk(), mockk()))) + matrixClient.givenUploadMediaResult(Result.failure(A_THROWABLE)) - val initialState = awaitItem() - initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + initialState.eventSink(ConfigureRoomEvents.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterCreateRoom = awaitItem() assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java) - assertThat(fakeAnalyticsService.capturedEvents.filterIsInstance()).isEmpty() + assertThat(analyticsService.capturedEvents.filterIsInstance()).isEmpty() - fakeMatrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL)) - stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + matrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL)) + stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Success::class.java) @@ -277,23 +284,25 @@ class ConfigureRoomPresenterTest { @Test fun `present - trigger retry and cancel actions`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val fakeMatrixClient = createMatrixClient() + val presenter = createConfigureRoomPresenter( + matrixClient = fakeMatrixClient + ) + presenter.test { + val initialState = initialState() val createRoomResult = Result.failure(A_THROWABLE) fakeMatrixClient.givenCreateRoomResult(createRoomResult) // Create - initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + initialState.eventSink(ConfigureRoomEvents.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterCreateRoom = awaitItem() assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java) assertThat((stateAfterCreateRoom.createRoomAction as? AsyncAction.Failure)?.error).isEqualTo(createRoomResult.exceptionOrNull()) // Retry - stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterRetry = awaitItem() @@ -305,4 +314,33 @@ class ConfigureRoomPresenterTest { assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) } } + + private suspend fun TurbineTestContext.initialState(): ConfigureRoomState { + skipItems(1) + return awaitItem() + } + + private fun createMatrixClient() = FakeMatrixClient( + userIdServerNameLambda = { "matrix.org" }, + ) + + private fun createConfigureRoomPresenter( + createRoomDataStore: CreateRoomDataStore = CreateRoomDataStore(UserListDataStore()), + matrixClient: MatrixClient = createMatrixClient(), + pickerProvider: PickerProvider = FakePickerProvider(), + mediaPreProcessor: MediaPreProcessor = FakeMediaPreProcessor(), + analyticsService: AnalyticsService = FakeAnalyticsService(), + permissionsPresenter: PermissionsPresenter = FakePermissionsPresenter(), + isKnockFeatureEnabled: Boolean = true, + ) = ConfigureRoomPresenter( + dataStore = createRoomDataStore, + matrixClient = matrixClient, + mediaPickerProvider = pickerProvider, + mediaPreProcessor = mediaPreProcessor, + analyticsService = analyticsService, + permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionsPresenter), + featureFlagService = FakeFeatureFlagService( + mapOf(FeatureFlags.Knock.key to isKnockFeatureEnabled) + ) + ) } From 59168e4c054de02b10c9c4895cb25405b8c61049 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 4 Nov 2024 16:25:14 +0100 Subject: [PATCH 15/50] create room : remove hardcoded strings and sync strings. --- .../createroom/impl/configureroom/ConfigureRoomView.kt | 8 ++++---- .../createroom/impl/configureroom/RoomAccessItem.kt | 10 +++++----- .../impl/src/main/res/values-be/translations.xml | 3 +++ .../impl/src/main/res/values-cs/translations.xml | 8 ++++++++ .../impl/src/main/res/values-el/translations.xml | 5 +++++ .../impl/src/main/res/values-et/translations.xml | 8 ++++++++ .../impl/src/main/res/values-fr/translations.xml | 8 ++++++++ .../impl/src/main/res/values-hu/translations.xml | 8 ++++++++ .../impl/src/main/res/values-in/translations.xml | 8 ++++++++ .../impl/src/main/res/values-nl/translations.xml | 5 +++++ .../impl/src/main/res/values-pl/translations.xml | 8 ++++++++ .../impl/src/main/res/values-pt/translations.xml | 8 ++++++++ .../impl/src/main/res/values-ru/translations.xml | 8 ++++++++ .../impl/src/main/res/values-sk/translations.xml | 8 ++++++++ .../createroom/impl/src/main/res/values/localazy.xml | 8 ++++++++ .../ui-strings/src/main/res/values-be/translations.xml | 3 --- .../ui-strings/src/main/res/values-cs/translations.xml | 8 -------- .../ui-strings/src/main/res/values-el/translations.xml | 5 ----- .../ui-strings/src/main/res/values-et/translations.xml | 8 -------- .../ui-strings/src/main/res/values-fr/translations.xml | 8 -------- .../ui-strings/src/main/res/values-hu/translations.xml | 8 -------- .../ui-strings/src/main/res/values-in/translations.xml | 8 -------- .../ui-strings/src/main/res/values-nl/translations.xml | 5 ----- .../ui-strings/src/main/res/values-pl/translations.xml | 8 -------- .../ui-strings/src/main/res/values-pt/translations.xml | 8 -------- .../ui-strings/src/main/res/values-ru/translations.xml | 8 -------- .../ui-strings/src/main/res/values-sk/translations.xml | 8 -------- libraries/ui-strings/src/main/res/values/localazy.xml | 8 -------- tools/localazy/config.json | 1 + 29 files changed, 103 insertions(+), 102 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index e4fd848a20..1101f9d35f 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -273,7 +273,7 @@ private fun RoomVisibilityOptions( modifier: Modifier = Modifier, ) { ConfigureRoomOptions( - title = "Room visibility", + title = stringResource(R.string.screen_create_room_room_visibility_section_title), modifier = modifier, verticalArrangement = Arrangement.spacedBy(16.dp), ) { @@ -294,7 +294,7 @@ private fun RoomAccessOptions( modifier: Modifier = Modifier, ) { ConfigureRoomOptions( - title = "Room access", + title = stringResource(R.string.screen_create_room_room_access_section_header), modifier = modifier, verticalArrangement = Arrangement.spacedBy(12.dp), ) { @@ -323,7 +323,7 @@ private fun RoomAddressField( modifier = Modifier.padding(horizontal = 16.dp), style = ElementTheme.typography.fontBodyMdRegular, color = MaterialTheme.colorScheme.primary, - text = "Room address", + text = stringResource(R.string.screen_create_room_room_address_section_title), ) TextField( @@ -346,7 +346,7 @@ private fun RoomAddressField( }, supportingText = { Text( - text = "In order for this room to be visible in the public room directory, you will need to a room address. ", + text = stringResource(R.string.screen_create_room_room_address_section_footer), style = ElementTheme.typography.fontBodySmRegular, color = ElementTheme.colors.textSecondary, ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccessItem.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccessItem.kt index 0e036de9b6..ce1e249396 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccessItem.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomAccessItem.kt @@ -8,18 +8,18 @@ package io.element.android.features.createroom.impl.configureroom import androidx.annotation.StringRes -import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.features.createroom.impl.R enum class RoomAccessItem( @StringRes val title: Int, @StringRes val description: Int ) { Anyone( - title = CommonStrings.screen_create_room_access_section_anyone_option_title, - description = CommonStrings.screen_create_room_access_section_anyone_option_description, + title = R.string.screen_create_room_room_access_section_anyone_option_title, + description = R.string.screen_create_room_room_access_section_anyone_option_description, ), AskToJoin( - title = CommonStrings.screen_create_room_access_section_knocking_option_title, - description = CommonStrings.screen_create_room_access_section_knocking_option_description, + title = R.string.screen_create_room_room_access_section_knocking_option_title, + description = R.string.screen_create_room_room_access_section_knocking_option_description, ), } diff --git a/features/createroom/impl/src/main/res/values-be/translations.xml b/features/createroom/impl/src/main/res/values-be/translations.xml index 2415fe6bb6..861fca4d05 100644 --- a/features/createroom/impl/src/main/res/values-be/translations.xml +++ b/features/createroom/impl/src/main/res/values-be/translations.xml @@ -7,6 +7,9 @@ "Прыватны пакой (толькі па запрашэнні)" "Паведамленні не зашыфраваны, і кожны можа іх прачытаць. Вы можаце ўключыць шыфраванне пазней." "Публічны пакой (для ўсіх)" + "Хто заўгодна" + "Доступ у пакой" + "Папрасіце далучыцца" "Назва пакоя" "Стварыце пакой" "Тэма (неабавязкова)" diff --git a/features/createroom/impl/src/main/res/values-cs/translations.xml b/features/createroom/impl/src/main/res/values-cs/translations.xml index 13ed58be07..697120201d 100644 --- a/features/createroom/impl/src/main/res/values-cs/translations.xml +++ b/features/createroom/impl/src/main/res/values-cs/translations.xml @@ -8,7 +8,15 @@ "Tuto místnost může najít kdokoli. To můžete kdykoli změnit v nastavení místnosti." "Veřejná místnost" + "Do této místnosti může vstoupit kdokoli" + "Kdokoliv" + "Přístup do místnosti" + "Kdokoli může požádat o vstup do místnosti, ale správce nebo moderátor bude muset žádost přijmout" + "Požádat o připojení" + "Aby byla tato místnost viditelná v adresáři veřejných místností, budete potřebovat adresu místnosti." + "Adresa místnosti" "Název místnosti" + "Viditelnost místnosti" "Vytvořit místnost" "Téma (nepovinné)" "Při pokusu o zahájení chatu došlo k chybě" diff --git a/features/createroom/impl/src/main/res/values-el/translations.xml b/features/createroom/impl/src/main/res/values-el/translations.xml index 82eda078f4..72e1d997db 100644 --- a/features/createroom/impl/src/main/res/values-el/translations.xml +++ b/features/createroom/impl/src/main/res/values-el/translations.xml @@ -8,6 +8,11 @@ "Ο καθένας μπορεί να βρει αυτό το δωμάτιο. Μπορείς να το αλλάξεις ανά πάσα στιγμή στις ρυθμίσεις δωματίου." "Δημόσιο δωμάτιο" + "Οποιοσδήποτε μπορεί να συμμετάσχει σε αυτό το δωμάτιο" + "Οποιοσδήποτε" + "Πρόσβαση Δωματίου" + "Οποιοσδήποτε μπορεί να ζητήσει να συμμετάσχει στο δωμάτιο, αλλά ένας διαχειριστής ή συντονιστής θα πρέπει να αποδεχθεί το αίτημα" + "Αίτημα συμμετοχής" "Όνομα δωματίου" "Δημιούργησε ένα δωμάτιο" "Θέμα (προαιρετικό)" diff --git a/features/createroom/impl/src/main/res/values-et/translations.xml b/features/createroom/impl/src/main/res/values-et/translations.xml index 043c228dea..67db887f2c 100644 --- a/features/createroom/impl/src/main/res/values-et/translations.xml +++ b/features/createroom/impl/src/main/res/values-et/translations.xml @@ -8,7 +8,15 @@ "Kõik saavad seda jututuba leida. Sa võid seda jututoa seadistustest alati muuta." "Avalik jututuba" + "Kõik võivad selle jututoaga liituda" + "Kõik" + "Ligipääs jututoale" + "Kõik võivad paluda selle jututoaga liitumist, kuid peakasutaja või moderaator peavad selle kinnitama" + "Küsi võimalust liitumiseks" + "Selleks, et see jututuba oleks nähtav jututubade avalikus kataloogis, sa vajad jututoa aadressi." + "Jututoa aadress" "Jututoa nimi" + "Jututoa nähtavus" "Loo jututuba" "Teema (kui soovid lisada)" "Vestluse alustamisel tekkis viga" diff --git a/features/createroom/impl/src/main/res/values-fr/translations.xml b/features/createroom/impl/src/main/res/values-fr/translations.xml index 732dfe89a0..af40d1f9fe 100644 --- a/features/createroom/impl/src/main/res/values-fr/translations.xml +++ b/features/createroom/impl/src/main/res/values-fr/translations.xml @@ -8,7 +8,15 @@ "N’importe qui peut trouver ce salon. Vous pouvez modifier cela à tout moment dans les paramètres du salon." "Salon public" + "Tout le monde peut rejoindre ce salon" + "Tout le monde" + "Accès au salon" + "Tout le monde peut demander à rejoindre le salon, mais un administrateur ou un modérateur devra accepter la demande" + "Demander à rejoindre" + "Pour que ce salon soit visible dans le répertoire des salons publics, vous aurez besoin d’une adresse de salon." + "Adresse du salon" "Nom du salon" + "Visibilité du salon" "Créer un salon" "Sujet (facultatif)" "Une erreur s’est produite lors de la tentative de création de la discussion" diff --git a/features/createroom/impl/src/main/res/values-hu/translations.xml b/features/createroom/impl/src/main/res/values-hu/translations.xml index a521ec214e..ee935c8c60 100644 --- a/features/createroom/impl/src/main/res/values-hu/translations.xml +++ b/features/createroom/impl/src/main/res/values-hu/translations.xml @@ -8,7 +8,15 @@ "Bárki megtalálhatja ezt a szobát. Ezt bármikor módosíthatja a szobabeállításokban." "Nyilvános szoba" + "Bárki csatlakozhat ehhez a szobához" + "Bárki" + "Szobahozzáférés" + "Bárki kérheti, hogy csatlakozzon a szobához, de egy adminisztrátornak vagy moderátornak el kell fogadnia a kérést" + "Csatlakozás kérése" + "Ahhoz, hogy ez a szoba látható legyen a nyilvános szobák címtárában, meg kell adnia a szoba címét." + "Szoba címe" "Szoba neve" + "Szoba láthatósága" "Szoba létrehozása" "Téma (nem kötelező)" "Hiba történt a csevegés indításakor" diff --git a/features/createroom/impl/src/main/res/values-in/translations.xml b/features/createroom/impl/src/main/res/values-in/translations.xml index a9f795983e..b1c7aeef1c 100644 --- a/features/createroom/impl/src/main/res/values-in/translations.xml +++ b/features/createroom/impl/src/main/res/values-in/translations.xml @@ -8,7 +8,15 @@ "Siapa pun dapat mencari ruangan ini. Anda dapat mengubah ini kapan pun dalam pengaturan ruangan." "Ruangan publik" + "Siapa pun dapat bergabung dengan ruangan ini" + "Siapa pun" + "Akses Ruangan" + "Siapa pun dapat meminta untuk bergabung dengan ruangan tetapi administrator atau moderator harus menerima permintaan tersebut" + "Minta untuk bergabung" + "Supaya ruangan ini terlihat di direktori ruangan publik, Anda memerlukan alamat ruangan." + "Alamat ruangan" "Nama ruangan" + "Keterlihatan ruangan" "Buat ruangan" "Topik (opsional)" "Terjadi kesalahan saat mencoba memulai obrolan" diff --git a/features/createroom/impl/src/main/res/values-nl/translations.xml b/features/createroom/impl/src/main/res/values-nl/translations.xml index 9421cface6..c1f0fd92f5 100644 --- a/features/createroom/impl/src/main/res/values-nl/translations.xml +++ b/features/createroom/impl/src/main/res/values-nl/translations.xml @@ -8,6 +8,11 @@ "Iedereen kan deze kamer vinden. Je kunt dit op elk gewenst moment wijzigen in de kamerinstellingen." "Openbare kamer" + "Iedereen kan toetreden tot deze kamer" + "Iedereen" + "Toegang tot de kamer" + "Iedereen kan vragen om toe te treden tot de kamer, maar een beheerder of moderator moet het verzoek accepteren" + "Vraag om toe te treden" "Naam van de kamer" "Creëer een kamer" "Onderwerp (optioneel)" diff --git a/features/createroom/impl/src/main/res/values-pl/translations.xml b/features/createroom/impl/src/main/res/values-pl/translations.xml index 3c3ad1fa12..7902fb4c3c 100644 --- a/features/createroom/impl/src/main/res/values-pl/translations.xml +++ b/features/createroom/impl/src/main/res/values-pl/translations.xml @@ -8,7 +8,15 @@ "Każdy może znaleźć ten pokój. Możesz to zmienić w ustawieniach pokoju." "Pokój publiczny" + "Każdy może dołączyć do tego pokoju" + "Wszyscy" + "Dostęp do pokoju" + "Każdy może poprosić o dołączenie do pokoju, ale administrator lub moderator będzie musiał zatwierdzić prośbę" + "Poproś o dołączenie" + "Aby ten pokój był widoczny w katalogu pomieszczeń publicznych, będziesz potrzebował adres pokoju." + "Adres pokoju" "Nazwa pokoju" + "Widoczność pomieszczenia" "Utwórz pokój" "Temat (opcjonalnie)" "Wystąpił błąd podczas próby rozpoczęcia czatu" diff --git a/features/createroom/impl/src/main/res/values-pt/translations.xml b/features/createroom/impl/src/main/res/values-pt/translations.xml index 019a6dd47c..60ee753b13 100644 --- a/features/createroom/impl/src/main/res/values-pt/translations.xml +++ b/features/createroom/impl/src/main/res/values-pt/translations.xml @@ -8,7 +8,15 @@ "Qualquer um pode encontrar esta sala. Pode alterar esta opção nas definições da sala." "Sala pública" + "Qualquer pessoa pode entrar nesta sala" + "Qualquer pessoa" + "Acesso à sala" + "Qualquer pessoa pode pedir para entrar na sala, mas um administrador ou um moderador terá de aceitar o pedido" + "Pedir para participar" + "Para que esta sala seja visível no diretório público de salas, precisas de um endereço de sala." + "Endereço da sala" "Nome da sala" + "Visibilidade da sala" "Criar uma sala" "Descrição (opcional)" "Ocorreu um erro ao tentar iniciar uma conversa" diff --git a/features/createroom/impl/src/main/res/values-ru/translations.xml b/features/createroom/impl/src/main/res/values-ru/translations.xml index 39df1f0ef5..b21bcb68ac 100644 --- a/features/createroom/impl/src/main/res/values-ru/translations.xml +++ b/features/createroom/impl/src/main/res/values-ru/translations.xml @@ -8,7 +8,15 @@ "Любой желающий может найти эту комнату. Вы можете изменить это в любое время в настройках комнаты." "Общедоступная комната" + "Любой желающий может присоединиться к этой комнате" + "Любой" + "Доступ в комнату" + "Любой желающий может подать заявку на присоединение к комнате, но администратор или модератор должен будет принять запрос." + "Попросить присоединиться" + "Чтобы эта комната была видна в каталоге общедоступных, вам необходим ее адрес" + "Адрес комнаты" "Название комнаты" + "Видимость комнаты" "Создать комнату" "Тема (необязательно)" "Произошла ошибка при запуске чата" diff --git a/features/createroom/impl/src/main/res/values-sk/translations.xml b/features/createroom/impl/src/main/res/values-sk/translations.xml index 12a94590c8..d2d742239f 100644 --- a/features/createroom/impl/src/main/res/values-sk/translations.xml +++ b/features/createroom/impl/src/main/res/values-sk/translations.xml @@ -8,7 +8,15 @@ "Túto miestnosť môže nájsť ktokoľvek. Môžete to kedykoľvek zmeniť v nastaveniach miestnosti." "Verejná miestnosť" + "Do tejto miestnosti sa môže pripojiť ktokoľvek" + "Ktokoľvek" + "Prístup do miestnosti" + "Ktokoľvek môže požiadať o pripojenie sa k miestnosti, ale administrátor alebo moderátor bude musieť žiadosť schváliť" + "Požiadať o pripojenie" + "Aby bola táto miestnosť viditeľná v adresári verejných miestností, budete potrebovať adresu miestnosti." + "Adresa miestnosti" "Názov miestnosti" + "Viditeľnosť miestnosti" "Vytvoriť miestnosť" "Téma (voliteľné)" "Pri pokuse o spustenie konverzácie sa vyskytla chyba" diff --git a/features/createroom/impl/src/main/res/values/localazy.xml b/features/createroom/impl/src/main/res/values/localazy.xml index e5256d928b..6ed5510ce0 100644 --- a/features/createroom/impl/src/main/res/values/localazy.xml +++ b/features/createroom/impl/src/main/res/values/localazy.xml @@ -8,7 +8,15 @@ "Anyone can find this room. You can change this anytime in room settings." "Public room" + "Anyone can join this room" + "Anyone" + "Room Access" + "Anyone can ask to join the room but an administrator or a moderator will have to accept the request" + "Ask to join" + "In order for this room to be visible in the public room directory, you will need a room address." + "Room address" "Room name" + "Room visibility" "Create a room" "Topic (optional)" "An error occurred when trying to start a chat" diff --git a/libraries/ui-strings/src/main/res/values-be/translations.xml b/libraries/ui-strings/src/main/res/values-be/translations.xml index 52272f43e3..38de3c79a9 100644 --- a/libraries/ui-strings/src/main/res/values-be/translations.xml +++ b/libraries/ui-strings/src/main/res/values-be/translations.xml @@ -284,9 +284,6 @@ "Гэй, пагавары са мной у %1$s: %2$s" "%1$s Android" "Паведаміць аб памылцы з дапамогай Rageshake" - "Хто заўгодна" - "Доступ у пакой" - "Папрасіце далучыцца" "Не ўдалося выбраць носьбіт, паўтарыце спробу." "Не атрымалася апрацаваць медыяфайл для загрузкі, паспрабуйце яшчэ раз." "Не атрымалася загрузіць медыяфайлы, паспрабуйце яшчэ раз." diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index bdf97164ba..421b5b8523 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -295,14 +295,6 @@ Důvod: %1$s." "Ahoj, ozvi se mi na %1$s: %2$s" "%1$s Android" "Zatřeste zařízením pro nahlášení chyby" - "Do této místnosti může vstoupit kdokoli" - "Kdokoliv" - "Přístup do místnosti" - "Kdokoli může požádat o vstup do místnosti, ale správce nebo moderátor bude muset žádost přijmout" - "Požádat o připojení" - "Aby byla tato místnost viditelná v adresáři veřejných místností, budete potřebovat adresu místnosti." - "Adresa místnosti" - "Viditelnost místnosti" "Výběr média se nezdařil, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." diff --git a/libraries/ui-strings/src/main/res/values-el/translations.xml b/libraries/ui-strings/src/main/res/values-el/translations.xml index e737fa106c..de35c4d797 100644 --- a/libraries/ui-strings/src/main/res/values-el/translations.xml +++ b/libraries/ui-strings/src/main/res/values-el/translations.xml @@ -285,11 +285,6 @@ "Γεια, μίλα μου στην εφαρμογή %1$s :%2$s" "%1$s Android" "Κούνησε δυνατά τη συσκευή σου για να αναφέρεις κάποιο σφάλμα" - "Οποιοσδήποτε μπορεί να συμμετάσχει σε αυτό το δωμάτιο" - "Οποιοσδήποτε" - "Πρόσβαση Δωματίου" - "Οποιοσδήποτε μπορεί να ζητήσει να συμμετάσχει στο δωμάτιο, αλλά ένας διαχειριστής ή συντονιστής θα πρέπει να αποδεχθεί το αίτημα" - "Αίτημα συμμετοχής" "Αποτυχία επιλογής πολυμέσου, δοκίμασε ξανά." "Αποτυχία μεταφόρτωσης μέσου, δοκίμασε ξανά." "Αποτυχία μεταφόρτωσης πολυμέσων, δοκίμασε ξανά." diff --git a/libraries/ui-strings/src/main/res/values-et/translations.xml b/libraries/ui-strings/src/main/res/values-et/translations.xml index 201cfd5165..c0607c60e4 100644 --- a/libraries/ui-strings/src/main/res/values-et/translations.xml +++ b/libraries/ui-strings/src/main/res/values-et/translations.xml @@ -291,14 +291,6 @@ Põhjus: %1$s." "Hei, suhtle minuga %1$s võrgus: %2$s" "%1$s Android" "Veast teatamiseks raputa nutiseadet ägedalt" - "Kõik võivad selle jututoaga liituda" - "Kõik" - "Ligipääs jututoale" - "Kõik võivad paluda selle jututoaga liitumist, kuid peakasutaja või moderaator peavad selle kinnitama" - "Küsi võimalust liitumiseks" - "Selleks, et see jututuba oleks nähtav jututubade avalikus kataloogis, sa vajad jututoa aadressi." - "Jututoa aadress" - "Jututoa nähtavus" "Meediafaili valimine ei õnnestunud. Palun proovi uuesti." "Meediafaili töötlemine enne üleslaadimist ei õnnestunud. Palun proovi uuesti." "Meediafaili üleslaadimine ei õnnestunud. Palun proovi uuesti." diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml index e26cc1ce05..f9159b6d6c 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -290,14 +290,6 @@ Raison: %1$s." "Salut, parle-moi sur %1$s : %2$s" "%1$s Android" "Rageshake pour signaler un problème" - "Tout le monde peut rejoindre ce salon" - "Tout le monde" - "Accès au salon" - "Tout le monde peut demander à rejoindre le salon, mais un administrateur ou un modérateur devra accepter la demande" - "Demander à rejoindre" - "Pour que ce salon soit visible dans le répertoire des salons publics, vous aurez besoin d’une adresse de salon." - "Adresse du salon" - "Visibilité du salon" "Échec de la sélection du média, veuillez réessayer." "Échec du traitement des médias à télécharger, veuillez réessayer." "Échec du téléchargement du média, veuillez réessayer." diff --git a/libraries/ui-strings/src/main/res/values-hu/translations.xml b/libraries/ui-strings/src/main/res/values-hu/translations.xml index fe376455ec..a80e8de9e5 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -291,14 +291,6 @@ Ok: %1$s." "Beszélgessünk itt: %1$s, %2$s" "%1$s Android" "Az eszköz rázása a hibajelentéshez" - "Bárki csatlakozhat ehhez a szobához" - "Bárki" - "Szobahozzáférés" - "Bárki kérheti, hogy csatlakozzon a szobához, de egy adminisztrátornak vagy moderátornak el kell fogadnia a kérést" - "Csatlakozás kérése" - "Ahhoz, hogy ez a szoba látható legyen a nyilvános szobák címtárában, meg kell adnia a szoba címét." - "Szoba címe" - "Szoba láthatósága" "Nem sikerült kiválasztani a médiát, próbálja újra." "Nem sikerült feldolgozni a feltöltendő médiát, próbálja újra." "Nem sikerült a média feltöltése, próbálja újra." diff --git a/libraries/ui-strings/src/main/res/values-in/translations.xml b/libraries/ui-strings/src/main/res/values-in/translations.xml index 48a619a009..a5beab01d3 100644 --- a/libraries/ui-strings/src/main/res/values-in/translations.xml +++ b/libraries/ui-strings/src/main/res/values-in/translations.xml @@ -287,14 +287,6 @@ Alasan: %1$s." "Hai, bicaralah dengan saya di %1$s: %2$s" "%1$s Android" "Rageshake untuk melaporkan kutu" - "Siapa pun dapat bergabung dengan ruangan ini" - "Siapa pun" - "Akses Ruangan" - "Siapa pun dapat meminta untuk bergabung dengan ruangan tetapi administrator atau moderator harus menerima permintaan tersebut" - "Minta untuk bergabung" - "Supaya ruangan ini terlihat di direktori ruangan publik, Anda memerlukan alamat ruangan." - "Alamat ruangan" - "Keterlihatan ruangan" "Gagal memilih media, silakan coba lagi." "Gagal memproses media untuk diunggah, silakan coba lagi." "Gagal mengunggah media, silakan coba lagi." diff --git a/libraries/ui-strings/src/main/res/values-nl/translations.xml b/libraries/ui-strings/src/main/res/values-nl/translations.xml index ec174da0f7..4b10c17180 100644 --- a/libraries/ui-strings/src/main/res/values-nl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-nl/translations.xml @@ -283,11 +283,6 @@ Reden: %1$s." "Hé, praat met me op %1$s: %2$s" "%1$s Android" "Schudden om een bug te melden" - "Iedereen kan toetreden tot deze kamer" - "Iedereen" - "Toegang tot de kamer" - "Iedereen kan vragen om toe te treden tot de kamer, maar een beheerder of moderator moet het verzoek accepteren" - "Vraag om toe te treden" "Het selecteren van media is mislukt. Probeer het opnieuw." "Het verwerken van media voor uploaden is mislukt. Probeer het opnieuw." "Het uploaden van media is mislukt. Probeer het opnieuw." diff --git a/libraries/ui-strings/src/main/res/values-pl/translations.xml b/libraries/ui-strings/src/main/res/values-pl/translations.xml index baef162dc9..77decc684f 100644 --- a/libraries/ui-strings/src/main/res/values-pl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pl/translations.xml @@ -292,14 +292,6 @@ Powód: %1$s." "Hej, porozmawiajmy na %1$s: %2$s" "%1$s Android" "Wstrząśnij gniewnie, aby zgłosić błąd" - "Każdy może dołączyć do tego pokoju" - "Wszyscy" - "Dostęp do pokoju" - "Każdy może poprosić o dołączenie do pokoju, ale administrator lub moderator będzie musiał zatwierdzić prośbę" - "Poproś o dołączenie" - "Aby ten pokój był widoczny w katalogu pomieszczeń publicznych, będziesz potrzebował adres pokoju." - "Adres pokoju" - "Widoczność pomieszczenia" "Nie udało się wybrać multimediów. Spróbuj ponownie." "Przetwarzanie multimediów do przesłania nie powiodło się, spróbuj ponownie." "Przesyłanie multimediów nie powiodło się, spróbuj ponownie." diff --git a/libraries/ui-strings/src/main/res/values-pt/translations.xml b/libraries/ui-strings/src/main/res/values-pt/translations.xml index 10c534064b..65e2a5d483 100644 --- a/libraries/ui-strings/src/main/res/values-pt/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pt/translations.xml @@ -291,14 +291,6 @@ Razão: %1$s." "Alô! Fala comigo na %1$s: %2$s" "%1$s Android" "Agita o dispositivo em fúria para comunicar um problema" - "Qualquer pessoa pode entrar nesta sala" - "Qualquer pessoa" - "Acesso à sala" - "Qualquer pessoa pode pedir para entrar na sala, mas um administrador ou um moderador terá de aceitar o pedido" - "Pedir para participar" - "Para que esta sala seja visível no diretório público de salas, precisas de um endereço de sala." - "Endereço da sala" - "Visibilidade da sala" "Falha ao selecionar multimédia, por favor tente novamente." "Falha ao processar multimédia para carregamento, por favor tente novamente." "Falhar ao carregar multimédia, por favor tente novamente." diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index dcf525d5fd..e6f3bc4767 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -295,14 +295,6 @@ "Привет, поговори со мной по %1$s: %2$s" "%1$s Android" "Встряхните устройство, чтобы сообщить об ошибке" - "Любой желающий может присоединиться к этой комнате" - "Любой" - "Доступ в комнату" - "Любой желающий может подать заявку на присоединение к комнате, но администратор или модератор должен будет принять запрос." - "Попросить присоединиться" - "Чтобы эта комната была видна в каталоге общедоступных, вам необходим ее адрес" - "Адрес комнаты" - "Видимость комнаты" "Не удалось выбрать носитель, попробуйте еще раз." "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." "Не удалось загрузить медиафайлы, попробуйте еще раз." diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index 3df443c6cf..3d1cc9d416 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -293,14 +293,6 @@ Dôvod: %1$s." "Ahoj, porozprávajte sa so mnou na %1$s: %2$s" "%1$s Android" "Zúrivo potriasť pre nahlásenie chyby" - "Do tejto miestnosti sa môže pripojiť ktokoľvek" - "Ktokoľvek" - "Prístup do miestnosti" - "Ktokoľvek môže požiadať o pripojenie sa k miestnosti, ale administrátor alebo moderátor bude musieť žiadosť schváliť" - "Požiadať o pripojenie" - "Aby bola táto miestnosť viditeľná v adresári verejných miestností, budete potrebovať adresu miestnosti." - "Adresa miestnosti" - "Viditeľnosť miestnosti" "Nepodarilo sa vybrať médium, skúste to prosím znova." "Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova." "Nepodarilo sa nahrať médiá, skúste to prosím znova." diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index ea1b0f05fd..d1f17f147f 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -291,14 +291,6 @@ Reason: %1$s." "Hey, talk to me on %1$s: %2$s" "%1$s Android" "Rageshake to report bug" - "Anyone can join this room" - "Anyone" - "Room Access" - "Anyone can ask to join the room but an administrator or a moderator will have to accept the request" - "Ask to join" - "In order for this room to be visible in the public room directory, you will need a room address." - "Room address" - "Room visibility" "Failed selecting media, please try again." "Failed processing media to upload, please try again." "Failed uploading media, please try again." diff --git a/tools/localazy/config.json b/tools/localazy/config.json index fb05345687..f18744bae9 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -61,6 +61,7 @@ "name" : ":features:createroom:impl", "includeRegex" : [ "screen_create_room_.*", + "screen\\.create_room\\..*", "screen_start_chat_.*" ] }, From 197cad9f8c8a5fae4557a671e8974b1027d20fdc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 16:39:33 +0000 Subject: [PATCH 16/50] fix(deps): update dependency androidx.constraintlayout:constraintlayout to v2.2.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f431a76e9b..12c0bf626b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,7 @@ core = "1.13.1" # due to the DefaultMigrationStore not behaving as expected. # Stick to 1.0.0 for now, and ensure that this scenario cannot be reproduced when upgrading the version. datastore = "1.0.0" -constraintlayout = "2.1.4" +constraintlayout = "2.2.0" constraintlayout_compose = "1.1.0" lifecycle = "2.8.6" activity = "1.9.3" From 0ebb21093c5365b080302b087a6db083a3aad00c Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 5 Nov 2024 08:44:20 +0000 Subject: [PATCH 17/50] Update screenshots --- ...s.createroom.impl.components_RoomAccessOption_Day_0_en.png | 3 +++ ...createroom.impl.components_RoomAccessOption_Night_0_en.png | 3 +++ ....createroom.impl.components_RoomPrivacyOption_Day_0_en.png | 4 ++-- ...reateroom.impl.components_RoomPrivacyOption_Night_0_en.png | 4 ++-- ...eateroom.impl.configureroom_ConfigureRoomView_Day_0_en.png | 4 ++-- ...eateroom.impl.configureroom_ConfigureRoomView_Day_1_en.png | 4 ++-- ...eateroom.impl.configureroom_ConfigureRoomView_Day_2_en.png | 3 +++ ...teroom.impl.configureroom_ConfigureRoomView_Night_0_en.png | 4 ++-- ...teroom.impl.configureroom_ConfigureRoomView_Night_1_en.png | 4 ++-- ...teroom.impl.configureroom_ConfigureRoomView_Night_2_en.png | 3 +++ 10 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomAccessOption_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomAccessOption_Night_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_2_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomAccessOption_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomAccessOption_Day_0_en.png new file mode 100644 index 0000000000..a45ad2fd47 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomAccessOption_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:39e248b3ba5e1c1b41d557471826f1d67d9c1d71b6b8ec34ca6226358dd396b5 +size 15609 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomAccessOption_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomAccessOption_Night_0_en.png new file mode 100644 index 0000000000..a5b7c14008 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomAccessOption_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81760d5d90b82dee085f30b6c20e259caaafc63a68033137ae28d1b2d8f2f121 +size 15125 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Day_0_en.png index dff2945ebf..453d07fd59 100644 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ef1c5aa8a1be3fc5c2fd55b8fde64f7ec7c3d12037e4e2b08049c6a1ea21a3d -size 30128 +oid sha256:43e7348faba79c517dc05d92f8bbc40a6bee7d977f8667ee6e945a7e722b20b5 +size 30728 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Night_0_en.png index ca8cc5e534..6e18ae0790 100644 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f5762dcb065b0406441e7f67d2b3a120f11bed71d1f56b96cb334b831d96ce3 -size 29511 +oid sha256:aec1f68cd88c087e22fae3f1334c6d7afaa085c71c8f121b4d653bca3bcfa8a6 +size 30074 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en.png index 8337a1e532..155986b58b 100644 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8344c5a707a3af3aaeab22006e84bd93f1cdf8389199d88beb276a8b672b0a4a -size 50817 +oid sha256:901462c6c5f40e95db357ad964aff83179e36182e0ec61b832fe73c76697257a +size 53949 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en.png index 0f46643716..871fa11194 100644 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c39135c177aa7b22c9451ef86bee41a216931759448ed2764d3bef4ea410230 -size 72685 +oid sha256:db6b448c513d3f173a988f1151a82af8455b00d822f75b76103067260ae1bac7 +size 76200 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_2_en.png new file mode 100644 index 0000000000..12208bc0e9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a2b4959fc4ca3a0b363b08e3724d0ad957f588320a69f901faaeb36d913b60f +size 76229 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en.png index 82896e9306..e349d30b1e 100644 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b05e067c92747ac92f10efcec20df0a1a7e1040c0be7a4b04009a5cf50c0aeb5 -size 49720 +oid sha256:90cd88d155bf54dc233d95c27c43d4ede4eccfd175840f592ff4882d3b2cb261 +size 52329 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en.png index 02e544dca1..f7483d3eb3 100644 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79b1423947e9f87d9139f1742f845a80d6474c0b36d3557b1c30fee6a3e808ad -size 71946 +oid sha256:0601ca5a9c3183e8244b5eaf1a14cf052afe15c61cd69a74472b9db4062caa3f +size 75345 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_2_en.png new file mode 100644 index 0000000000..86368097a9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bcb08e5d84df7a89e4851d631f1ad7b2bce6213cfd9be37d98782d1d778d0b23 +size 75375 From 75bd7457f339aed0eded5fd465e4837615052797 Mon Sep 17 00:00:00 2001 From: mxandreas Date: Tue, 5 Nov 2024 11:03:48 +0200 Subject: [PATCH 18/50] Update project status Update the project status to reflect that we're now in a phase of rollout and migrating. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f838a0fe50..5ae9b0c2b4 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ # Element X Android -Element X Android is a [Matrix](https://matrix.org/) Android Client provided by [element.io](https://element.io/). This app is currently in a pre-alpha release stage with only basic functionalities. +Element X Android is a [Matrix](https://matrix.org/) Android Client provided by [element.io](https://element.io/). The application is a total rewrite of [Element-Android](https://github.com/element-hq/element-android) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running Android 7+. The UI layer is written using [Jetpack Compose](https://developer.android.com/jetpack/compose), and the navigation is managed using [Appyx](https://github.com/bumble-tech/appyx). @@ -71,7 +71,7 @@ We're doing this as a way to share code between platforms and while we've seen p ## Status -This project is in work in progress. The app does not cover yet all functionalities we expect. The list of supported features can be found in [this issue](https://github.com/element-hq/element-x-android/issues/911). +This project is in an early rollout and migration phase. ## Contributing From d68afd2cbec71785c1685f136ed95cc97d6e2b39 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 20:04:52 +0000 Subject: [PATCH 19/50] fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v0.2.59 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 12c0bf626b..4737ef942a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -173,7 +173,7 @@ jsoup = "org.jsoup:jsoup:1.18.1" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0" timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.58" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.59" matrix_richtexteditor = { module = "io.element.android:wysiwyg", 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" } From ed1d208d9c0d160bf60ad08d32abbb1834b23537 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 5 Nov 2024 21:10:20 +0100 Subject: [PATCH 20/50] create room : fix konsist test --- .../features/createroom/impl/components/RoomVisibilityOption.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt index 15ae5c11a1..e76296fa4c 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt @@ -91,7 +91,7 @@ fun RoomVisibilityOption( @PreviewsDayNight @Composable -internal fun RoomPrivacyOptionPreview() = ElementPreview { +internal fun RoomVisibilityOptionPreview() = ElementPreview { val aRoomPrivacyItem = RoomVisibilityItem.Private Column { RoomVisibilityOption( From 4ff1e7962d76ee6e5cb6b743a7498525b70ae704 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 5 Nov 2024 21:12:39 +0100 Subject: [PATCH 21/50] create room : override invite power level for knockable rooms --- .../android/libraries/matrix/impl/RustMatrixClient.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 3417c9b377..d734d7e44b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -33,6 +33,7 @@ import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.PendingRoom +import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias import io.element.android.libraries.matrix.api.room.preview.RoomPreview @@ -320,7 +321,14 @@ class RustMatrixClient( }, invite = createRoomParams.invite?.map { it.value }, avatar = createRoomParams.avatar, - powerLevelContentOverride = defaultRoomCreationPowerLevels, + powerLevelContentOverride = defaultRoomCreationPowerLevels.copy( + invite = if (createRoomParams.joinRuleOverride == JoinRuleOverride.Knock) { + // override the invite power level so it's the same as kick. + RoomMember.Role.MODERATOR.powerLevel.toInt() + } else { + null + } + ), joinRuleOverride = when (createRoomParams.joinRuleOverride) { JoinRuleOverride.Knock -> RustJoinRule.Knock JoinRuleOverride.None -> null From 58e66963d8a183d2860daf95739c397047c8be85 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 5 Nov 2024 17:39:39 +0100 Subject: [PATCH 22/50] Hide the join call button if the user is already in the call. This is at the account level so if the user has joined the call on another device, the join button will be hidden. Extract room call state presenter to its own module and update RoomCallState model. Let RoomDetailsPresenter use the new RoomCallStatePresenter --- features/messages/impl/build.gradle.kts | 1 + .../messages/impl/MessagesPresenter.kt | 14 +- .../features/messages/impl/MessagesState.kt | 9 +- .../messages/impl/MessagesStateProvider.kt | 11 +- .../features/messages/impl/MessagesView.kt | 33 +---- .../list/PinnedMessagesListPresenter.kt | 4 +- .../impl/timeline/TimelinePresenter.kt | 8 +- .../messages/impl/timeline/TimelineState.kt | 3 +- .../impl/timeline/TimelineStateProvider.kt | 3 +- .../impl/timeline/components/CallMenuItem.kt | 120 ++++++++++++++++++ .../timeline/components/JoinCallMenuItem.kt | 52 -------- .../components/TimelineItemCallNotifyView.kt | 31 ++--- .../timeline/components/TimelineItemRow.kt | 2 +- .../messages/impl/MessagesPresenterTest.kt | 23 +--- features/roomcall/api/build.gradle.kts | 20 +++ .../features/roomcall/api/RoomCallState.kt | 29 +++++ .../roomcall/api/RoomCallStateProvider.kt | 34 +++++ features/roomcall/impl/build.gradle.kts | 36 ++++++ .../roomcall/impl/RoomCallStatePresenter.kt | 43 +++++++ .../roomcall/impl/di/RoomCallModule.kt | 23 ++++ .../impl/RoomCallStatePresenterTest.kt | 46 +++++++ features/roomdetails/impl/build.gradle.kts | 1 + .../roomdetails/impl/RoomDetailsPresenter.kt | 9 +- .../roomdetails/impl/RoomDetailsState.kt | 3 +- .../impl/RoomDetailsStateProvider.kt | 8 +- .../roomdetails/impl/RoomDetailsView.kt | 4 +- .../impl/RoomDetailsPresenterTest.kt | 2 + 27 files changed, 419 insertions(+), 153 deletions(-) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/CallMenuItem.kt delete mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/JoinCallMenuItem.kt create mode 100644 features/roomcall/api/build.gradle.kts create mode 100644 features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallState.kt create mode 100644 features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStateProvider.kt create mode 100644 features/roomcall/impl/build.gradle.kts create mode 100644 features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt create mode 100644 features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/di/RoomCallModule.kt create mode 100644 features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index 892c69ea2c..824e8c1692 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { implementation(projects.features.call.api) implementation(projects.features.location.api) implementation(projects.features.poll.api) + implementation(projects.features.roomcall.api) implementation(projects.libraries.androidutils) implementation(projects.libraries.core) implementation(projects.libraries.architecture) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index b215fc49fd..7840c307c7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -50,6 +50,7 @@ import io.element.android.features.messages.impl.timeline.protection.TimelinePro import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkStatus +import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.androidutils.clipboard.ClipboardHelper import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter @@ -75,7 +76,6 @@ import io.element.android.libraries.matrix.api.room.powerlevels.canSendMessage import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.ui.messages.reply.map import io.element.android.libraries.matrix.ui.model.getAvatarData -import io.element.android.libraries.matrix.ui.room.canCall import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.api.AnalyticsService @@ -98,6 +98,7 @@ class MessagesPresenter @AssistedInject constructor( private val reactionSummaryPresenter: Presenter, private val readReceiptBottomSheetPresenter: Presenter, private val pinnedMessagesBannerPresenter: Presenter, + private val roomCallStatePresenter: Presenter, private val networkMonitor: NetworkMonitor, private val snackbarDispatcher: SnackbarDispatcher, private val dispatchers: CoroutineDispatchers, @@ -133,6 +134,7 @@ class MessagesPresenter @AssistedInject constructor( val reactionSummaryState = reactionSummaryPresenter.present() val readReceiptBottomSheetState = readReceiptBottomSheetPresenter.present() val pinnedMessagesBannerState = pinnedMessagesBannerPresenter.present() + val roomCallState = roomCallStatePresenter.present() val syncUpdateFlow = room.syncUpdateFlow.collectAsState() @@ -152,8 +154,6 @@ class MessagesPresenter @AssistedInject constructor( mutableStateOf(false) } - val canJoinCall by room.canCall(updateKey = syncUpdateFlow.value) - LaunchedEffect(Unit) { // Remove the unread flag on entering but don't send read receipts // as those will be handled by the timeline. @@ -204,12 +204,6 @@ class MessagesPresenter @AssistedInject constructor( } } - val callState = when { - !canJoinCall -> RoomCallState.DISABLED - roomInfo?.hasRoomCall == true -> RoomCallState.ONGOING - else -> RoomCallState.ENABLED - } - return MessagesState( roomId = room.roomId, roomName = roomName, @@ -232,7 +226,7 @@ class MessagesPresenter @AssistedInject constructor( enableTextFormatting = MessageComposerConfig.ENABLE_RICH_TEXT_EDITING, enableVoiceMessages = enableVoiceMessages, appName = buildMeta.applicationName, - callState = callState, + roomCallState = roomCallState, pinnedMessagesBannerState = pinnedMessagesBannerState, eventSink = { handleEvents(it) } ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt index 2dc43030a4..a643784f1d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt @@ -18,6 +18,7 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState +import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage @@ -46,14 +47,8 @@ data class MessagesState( val showReinvitePrompt: Boolean, val enableTextFormatting: Boolean, val enableVoiceMessages: Boolean, - val callState: RoomCallState, + val roomCallState: RoomCallState, val appName: String, val pinnedMessagesBannerState: PinnedMessagesBannerState, val eventSink: (MessagesEvents) -> Unit ) - -enum class RoomCallState { - ENABLED, - ONGOING, - DISABLED -} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index 5d7ac33e5e..e8dc5329f4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -33,6 +33,9 @@ import io.element.android.features.messages.impl.timeline.protection.aTimelinePr import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessageComposerState import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessagePreviewState +import io.element.android.features.roomcall.api.RoomCallState +import io.element.android.features.roomcall.api.aStandByCallState +import io.element.android.features.roomcall.api.anOngoingCallState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize @@ -70,7 +73,7 @@ open class MessagesStateProvider : PreviewParameterProvider { ), ), aMessagesState( - callState = RoomCallState.ONGOING, + roomCallState = anOngoingCallState(), ), aMessagesState( enableVoiceMessages = true, @@ -80,7 +83,7 @@ open class MessagesStateProvider : PreviewParameterProvider { ), ), aMessagesState( - callState = RoomCallState.DISABLED, + roomCallState = aStandByCallState(canStartCall = false), ), aMessagesState( pinnedMessagesBannerState = aLoadedPinnedMessagesBannerState( @@ -115,7 +118,7 @@ fun aMessagesState( hasNetworkConnection: Boolean = true, showReinvitePrompt: Boolean = false, enableVoiceMessages: Boolean = true, - callState: RoomCallState = RoomCallState.ENABLED, + roomCallState: RoomCallState = aStandByCallState(), pinnedMessagesBannerState: PinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(), eventSink: (MessagesEvents) -> Unit = {}, ) = MessagesState( @@ -139,7 +142,7 @@ fun aMessagesState( showReinvitePrompt = showReinvitePrompt, enableTextFormatting = true, enableVoiceMessages = enableVoiceMessages, - callState = callState, + roomCallState = roomCallState, appName = "Element", pinnedMessagesBannerState = pinnedMessagesBannerState, eventSink = eventSink, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index bd8ae98b2f..e5d73fbed6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -52,7 +52,6 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme -import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.actionlist.ActionListEvents import io.element.android.features.messages.impl.actionlist.ActionListView import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction @@ -69,7 +68,7 @@ import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBan import io.element.android.features.messages.impl.timeline.FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.TimelineView -import io.element.android.features.messages.impl.timeline.components.JoinCallMenuItem +import io.element.android.features.messages.impl.timeline.components.CallMenuItem import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionBottomSheet import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents @@ -81,6 +80,7 @@ import io.element.android.features.messages.impl.voicemessages.composer.VoiceMes import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessagePermissionRationaleDialog import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageSendingFailedDialog import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView +import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.androidutils.ui.hideKeyboard import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule import io.element.android.libraries.designsystem.components.ProgressDialog @@ -93,8 +93,6 @@ import io.element.android.libraries.designsystem.components.dialogs.Confirmation import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.BottomSheetDragHandle -import io.element.android.libraries.designsystem.theme.components.Icon -import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar @@ -190,7 +188,7 @@ fun MessagesView( roomName = state.roomName.dataOrNull(), roomAvatar = state.roomAvatar.dataOrNull(), heroes = state.heroes, - callState = state.callState, + roomCallState = state.roomCallState, onBackClick = { // Since the textfield is now based on an Android view, this is no longer done automatically. // We need to hide the keyboard when navigating out of this screen. @@ -479,7 +477,7 @@ private fun MessagesViewTopBar( roomName: String?, roomAvatar: AvatarData?, heroes: ImmutableList, - callState: RoomCallState, + roomCallState: RoomCallState, onRoomDetailsClick: () -> Unit, onJoinCallClick: () -> Unit, onBackClick: () -> Unit, @@ -509,9 +507,8 @@ private fun MessagesViewTopBar( }, actions = { CallMenuItem( - isCallOngoing = callState == RoomCallState.ONGOING, - onClick = onJoinCallClick, - enabled = callState != RoomCallState.DISABLED + roomCallState = roomCallState, + onJoinCallClick = onJoinCallClick, ) Spacer(Modifier.width(8.dp)) }, @@ -519,24 +516,6 @@ private fun MessagesViewTopBar( ) } -@Composable -private fun CallMenuItem( - isCallOngoing: Boolean, - enabled: Boolean = true, - onClick: () -> Unit, -) { - if (isCallOngoing) { - JoinCallMenuItem(onJoinCallClick = onClick) - } else { - IconButton(onClick = onClick, enabled = enabled) { - Icon( - imageVector = CompoundIcons.VideoCallSolid(), - contentDescription = stringResource(CommonStrings.a11y_start_call), - ) - } - } -} - @Composable private fun RoomAvatarAndNameRow( roomName: String, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt index 4ccef0f23d..4673ae57b2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt @@ -32,6 +32,7 @@ import io.element.android.features.messages.impl.timeline.factories.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState import io.element.android.features.messages.impl.typing.TypingNotificationState +import io.element.android.features.roomcall.api.aStandByCallState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher @@ -89,7 +90,8 @@ class PinnedMessagesListPresenter @AssistedInject constructor( // We don't need to compute those values userHasPermissionToSendMessage = false, userHasPermissionToSendReaction = false, - isCallOngoing = false, + // We do not care about the call state here. + roomCallState = aStandByCallState(), // don't compute this value or the pin icon will be shown pinnedEventIds = emptyList(), typingNotificationState = TypingNotificationState( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index b40e24b88a..78ee91ed0a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -32,8 +32,8 @@ import io.element.android.features.messages.impl.typing.TypingNotificationState import io.element.android.features.messages.impl.voicemessages.timeline.RedactedVoiceMessageManager import io.element.android.features.poll.api.actions.EndPollAction import io.element.android.features.poll.api.actions.SendPollResponseAction +import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.core.bool.orFalse 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.UniqueId @@ -73,6 +73,7 @@ class TimelinePresenter @AssistedInject constructor( private val timelineItemIndexer: TimelineItemIndexer = TimelineItemIndexer(), private val resolveVerifiedUserSendFailurePresenter: Presenter, private val typingNotificationPresenter: Presenter, + private val roomCallStatePresenter: Presenter, ) : Presenter { @AssistedFactory interface Factory { @@ -229,14 +230,15 @@ class TimelinePresenter @AssistedInject constructor( } val typingNotificationState = typingNotificationPresenter.present() - val timelineRoomInfo by remember(typingNotificationState) { + val roomCallState = roomCallStatePresenter.present() + val timelineRoomInfo by remember(typingNotificationState, roomCallState) { derivedStateOf { TimelineRoomInfo( name = room.displayName, isDm = room.isDm, userHasPermissionToSendMessage = userHasPermissionToSendMessage, userHasPermissionToSendReaction = userHasPermissionToSendReaction, - isCallOngoing = roomInfo?.hasRoomCall.orFalse(), + roomCallState = roomCallState, pinnedEventIds = roomInfo?.pinnedEventIds.orEmpty(), typingNotificationState = typingNotificationState, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt index bfb357b579..aad5b7e354 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt @@ -12,6 +12,7 @@ import io.element.android.features.messages.impl.crypto.sendfailure.resolve.Reso import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.typing.TypingNotificationState +import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield @@ -73,7 +74,7 @@ data class TimelineRoomInfo( val name: String?, val userHasPermissionToSendMessage: Boolean, val userHasPermissionToSendReaction: Boolean, - val isCallOngoing: Boolean, + val roomCallState: RoomCallState, val pinnedEventIds: List, val typingNotificationState: TypingNotificationState, ) 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 6c790d7b1d..4d7677560e 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 @@ -23,6 +23,7 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI import io.element.android.features.messages.impl.timeline.model.virtual.aTimelineItemDaySeparatorModel import io.element.android.features.messages.impl.typing.TypingNotificationState import io.element.android.features.messages.impl.typing.aTypingNotificationState +import io.element.android.features.roomcall.api.aStandByCallState 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 @@ -249,7 +250,7 @@ internal fun aTimelineRoomInfo( name = name, userHasPermissionToSendMessage = userHasPermissionToSendMessage, userHasPermissionToSendReaction = true, - isCallOngoing = false, + roomCallState = aStandByCallState(), pinnedEventIds = pinnedEventIds, typingNotificationState = typingNotificationState, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/CallMenuItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/CallMenuItem.kt new file mode 100644 index 0000000000..58a3940475 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/CallMenuItem.kt @@ -0,0 +1,120 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.messages.impl.timeline.components + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.roomcall.api.RoomCallState +import io.element.android.features.roomcall.api.RoomCallStateProvider +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 +import io.element.android.libraries.designsystem.theme.components.IconButton +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.ui.strings.CommonStrings + +@Composable +internal fun CallMenuItem( + roomCallState: RoomCallState, + onJoinCallClick: () -> Unit, + modifier: Modifier = Modifier, +) { + when (roomCallState) { + is RoomCallState.StandBy -> { + StandByCallMenuItem( + roomCallState = roomCallState, + onJoinCallClick = onJoinCallClick, + modifier = modifier, + ) + } + is RoomCallState.OnGoing -> { + OnGoingCallMenuItem( + roomCallState = roomCallState, + onJoinCallClick = onJoinCallClick, + modifier = modifier, + ) + } + } +} + +@Composable +private fun StandByCallMenuItem( + roomCallState: RoomCallState.StandBy, + onJoinCallClick: () -> Unit, + modifier: Modifier = Modifier, +) { + IconButton( + modifier = modifier, + onClick = onJoinCallClick, + enabled = roomCallState.canStartCall, + ) { + Icon( + imageVector = CompoundIcons.VideoCallSolid(), + contentDescription = stringResource(CommonStrings.a11y_start_call), + ) + } +} + +@Composable +private fun OnGoingCallMenuItem( + roomCallState: RoomCallState.OnGoing, + onJoinCallClick: () -> Unit, + modifier: Modifier = Modifier, +) { + if (!roomCallState.isUserInTheCall) { + Button( + onClick = onJoinCallClick, + colors = ButtonDefaults.buttonColors( + contentColor = ElementTheme.colors.bgCanvasDefault, + containerColor = ElementTheme.colors.iconAccentTertiary + ), + contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp), + modifier = modifier.heightIn(min = 36.dp), + enabled = roomCallState.canJoinCall, + ) { + Icon( + modifier = Modifier.size(20.dp), + imageVector = CompoundIcons.VideoCallSolid(), + contentDescription = null + ) + Spacer(Modifier.width(8.dp)) + Text( + text = stringResource(CommonStrings.action_join), + style = ElementTheme.typography.fontBodyMdMedium + ) + Spacer(Modifier.width(8.dp)) + } + } else { + // Else user is already in the call, hide the button. + Box(modifier) + } +} + +@PreviewsDayNight +@Composable +internal fun CallMenuItemPreview( + @PreviewParameter(RoomCallStateProvider::class) roomCallState: RoomCallState +) = ElementPreview { + CallMenuItem( + roomCallState = roomCallState, + onJoinCallClick = {} + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/JoinCallMenuItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/JoinCallMenuItem.kt deleted file mode 100644 index 80611ccd7d..0000000000 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/JoinCallMenuItem.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only - * Please see LICENSE in the repository root for full details. - */ - -package io.element.android.features.messages.impl.timeline.components - -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import io.element.android.compound.theme.ElementTheme -import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.libraries.designsystem.theme.components.Icon -import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.ui.strings.CommonStrings - -@Composable -internal fun JoinCallMenuItem( - onJoinCallClick: () -> Unit, -) { - Button( - onClick = onJoinCallClick, - colors = ButtonDefaults.buttonColors( - contentColor = ElementTheme.colors.bgCanvasDefault, - containerColor = ElementTheme.colors.iconAccentTertiary - ), - contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp), - modifier = Modifier.heightIn(min = 36.dp), - ) { - Icon( - modifier = Modifier.size(20.dp), - imageVector = CompoundIcons.VideoCallSolid(), - contentDescription = null - ) - Spacer(Modifier.width(8.dp)) - Text( - text = stringResource(CommonStrings.action_join), - style = ElementTheme.typography.fontBodyMdMedium - ) - Spacer(Modifier.width(8.dp)) - } -} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt index d64430e087..ca499336f9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt @@ -31,6 +31,8 @@ import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemCallNotifyContent +import io.element.android.features.roomcall.api.RoomCallState +import io.element.android.features.roomcall.api.RoomCallStateProvider import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -41,7 +43,7 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable internal fun TimelineItemCallNotifyView( event: TimelineItem.Event, - isCallOngoing: Boolean, + roomCallState: RoomCallState, onLongClick: (TimelineItem.Event) -> Unit, onJoinCallClick: () -> Unit, modifier: Modifier = Modifier @@ -82,8 +84,11 @@ internal fun TimelineItemCallNotifyView( ) } } - if (isCallOngoing) { - JoinCallMenuItem(onJoinCallClick) + if (roomCallState is RoomCallState.OnGoing) { + CallMenuItem( + roomCallState = roomCallState, + onJoinCallClick = onJoinCallClick, + ) } else { Text( text = event.sentTime, @@ -101,18 +106,14 @@ internal fun TimelineItemCallNotifyView( internal fun TimelineItemCallNotifyViewPreview() { ElementPreview { Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) { - TimelineItemCallNotifyView( - event = aTimelineItemEvent(content = TimelineItemCallNotifyContent()), - isCallOngoing = true, - onLongClick = {}, - onJoinCallClick = {}, - ) - TimelineItemCallNotifyView( - event = aTimelineItemEvent(content = TimelineItemCallNotifyContent()), - isCallOngoing = false, - onLongClick = {}, - onJoinCallClick = {}, - ) + RoomCallStateProvider().values.forEach { roomCallState -> + TimelineItemCallNotifyView( + event = aTimelineItemEvent(content = TimelineItemCallNotifyContent()), + roomCallState = roomCallState, + onLongClick = {}, + onJoinCallClick = {}, + ) + } } } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt index c7c1cb5350..13c247645b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt @@ -105,7 +105,7 @@ internal fun TimelineItemRow( TimelineItemCallNotifyView( modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp), event = timelineItem, - isCallOngoing = timelineRoomInfo.isCallOngoing, + roomCallState = timelineRoomInfo.roomCallState, onLongClick = onLongClick, onJoinCallClick = onJoinCallClick, ) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index 9c6797cfc9..8d143ff179 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -37,6 +37,7 @@ import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvi import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.features.poll.api.actions.EndPollAction import io.element.android.features.poll.test.actions.FakeEndPollAction +import io.element.android.features.roomcall.api.aStandByCallState import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter @@ -139,27 +140,6 @@ class MessagesPresenterTest { } } - @Test - fun `present - call is disabled if user cannot join it even if there is an ongoing call`() = runTest { - val room = FakeMatrixRoom( - canUserJoinCallResult = { Result.success(false) }, - canUserSendMessageResult = { _, _ -> Result.success(true) }, - canRedactOwnResult = { Result.success(true) }, - canRedactOtherResult = { Result.success(true) }, - typingNoticeResult = { Result.success(Unit) }, - canUserPinUnpinResult = { Result.success(true) }, - ).apply { - givenRoomInfo(aRoomInfo(hasRoomCall = true)) - } - val presenter = createMessagesPresenter(matrixRoom = room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = consumeItemsUntilTimeout().last() - assertThat(initialState.callState).isEqualTo(RoomCallState.DISABLED) - } - } - @Test fun `present - handle toggling a reaction`() = runTest { val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true) @@ -1030,6 +1010,7 @@ class MessagesPresenterTest { readReceiptBottomSheetPresenter = { aReadReceiptBottomSheetState() }, identityChangeStatePresenter = { anIdentityChangeState() }, pinnedMessagesBannerPresenter = { aLoadedPinnedMessagesBannerState() }, + roomCallStatePresenter = { aStandByCallState() }, networkMonitor = FakeNetworkMonitor(), snackbarDispatcher = SnackbarDispatcher(), navigator = navigator, diff --git a/features/roomcall/api/build.gradle.kts b/features/roomcall/api/build.gradle.kts new file mode 100644 index 0000000000..12a2117b16 --- /dev/null +++ b/features/roomcall/api/build.gradle.kts @@ -0,0 +1,20 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.features.roomcall.api" +} + +dependencies { + implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) + implementation(libs.androidx.compose.ui.tooling.preview) +} diff --git a/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallState.kt b/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallState.kt new file mode 100644 index 0000000000..77c58fee2c --- /dev/null +++ b/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallState.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.roomcall.api + +import androidx.compose.runtime.Immutable +import io.element.android.features.roomcall.api.RoomCallState.OnGoing +import io.element.android.features.roomcall.api.RoomCallState.StandBy + +@Immutable +sealed interface RoomCallState { + data class StandBy( + val canStartCall: Boolean, + ) : RoomCallState + + data class OnGoing( + val canJoinCall: Boolean, + val isUserInTheCall: Boolean, + ) : RoomCallState +} + +fun RoomCallState.hasPermissionToJoin() = when (this) { + is StandBy -> canStartCall + is OnGoing -> canJoinCall +} diff --git a/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStateProvider.kt b/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStateProvider.kt new file mode 100644 index 0000000000..dce722c2c4 --- /dev/null +++ b/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStateProvider.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.roomcall.api + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider + +open class RoomCallStateProvider : PreviewParameterProvider { + override val values: Sequence = sequenceOf( + aStandByCallState(), + aStandByCallState(canStartCall = false), + anOngoingCallState(), + anOngoingCallState(canJoinCall = false), + anOngoingCallState(canJoinCall = true, isUserInTheCall = true), + ) +} + +fun anOngoingCallState( + canJoinCall: Boolean = true, + isUserInTheCall: Boolean = false, +) = RoomCallState.OnGoing( + canJoinCall = canJoinCall, + isUserInTheCall = isUserInTheCall, +) + +fun aStandByCallState( + canStartCall: Boolean = true, +) = RoomCallState.StandBy( + canStartCall = canStartCall, +) diff --git a/features/roomcall/impl/build.gradle.kts b/features/roomcall/impl/build.gradle.kts new file mode 100644 index 0000000000..e8f65b160b --- /dev/null +++ b/features/roomcall/impl/build.gradle.kts @@ -0,0 +1,36 @@ +import extension.setupAnvil + +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +plugins { + id("io.element.android-compose-library") +} + +android { + namespace = "io.element.android.features.roomcall.impl" +} + +setupAnvil() + +dependencies { + api(projects.features.roomcall.api) + implementation(libs.kotlinx.collections.immutable) + implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) + implementation(projects.libraries.matrixui) + + testImplementation(libs.test.junit) + testImplementation(libs.coroutines.test) + testImplementation(libs.molecule.runtime) + testImplementation(libs.test.truth) + testImplementation(libs.test.turbine) + testImplementation(projects.libraries.matrix.test) + testImplementation(projects.tests.testutils) + testImplementation(libs.androidx.compose.ui.test.junit) + testReleaseImplementation(libs.androidx.compose.ui.test.manifest) +} diff --git a/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt b/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt new file mode 100644 index 0000000000..175592af08 --- /dev/null +++ b/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.roomcall.impl + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import io.element.android.features.roomcall.api.RoomCallState +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.ui.room.canCall +import javax.inject.Inject + +class RoomCallStatePresenter @Inject constructor( + private val room: MatrixRoom, +) : Presenter { + @Composable + override fun present(): RoomCallState { + val roomInfo by room.roomInfoFlow.collectAsState(null) + val syncUpdateFlow = room.syncUpdateFlow.collectAsState() + val canJoinCall by room.canCall(updateKey = syncUpdateFlow.value) + val isUserInTheCall by remember { + derivedStateOf { + room.sessionId in roomInfo?.activeRoomCallParticipants.orEmpty() + } + } + val callState = when { + roomInfo?.hasRoomCall == true -> RoomCallState.OnGoing( + canJoinCall = canJoinCall, + isUserInTheCall = isUserInTheCall, + ) + else -> RoomCallState.StandBy(canStartCall = canJoinCall) + } + return callState + } +} diff --git a/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/di/RoomCallModule.kt b/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/di/RoomCallModule.kt new file mode 100644 index 0000000000..34c8d2448f --- /dev/null +++ b/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/di/RoomCallModule.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.roomcall.impl.di + +import com.squareup.anvil.annotations.ContributesTo +import dagger.Binds +import dagger.Module +import io.element.android.features.roomcall.api.RoomCallState +import io.element.android.features.roomcall.impl.RoomCallStatePresenter +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.di.RoomScope + +@ContributesTo(RoomScope::class) +@Module +interface RoomCallModule { + @Binds + fun bindRoomCallStatePresenter(presenter: RoomCallStatePresenter): Presenter +} diff --git a/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt b/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt new file mode 100644 index 0000000000..ae6b062738 --- /dev/null +++ b/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.roomcall.impl + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.roomcall.api.RoomCallState +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomInfo +import io.element.android.tests.testutils.test +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class RoomCallStatePresenterTest { + @Test + fun `present - call is disabled if user cannot join it even if there is an ongoing call`() = runTest { + val room = FakeMatrixRoom( + canUserJoinCallResult = { Result.success(false) }, + canUserSendMessageResult = { _, _ -> Result.success(true) }, + canRedactOwnResult = { Result.success(true) }, + canRedactOtherResult = { Result.success(true) }, + typingNoticeResult = { Result.success(Unit) }, + canUserPinUnpinResult = { Result.success(true) }, + ).apply { + givenRoomInfo(aRoomInfo(hasRoomCall = true)) + } + val presenter = createRoomCallStatePresenter(matrixRoom = room) + presenter.test { + val initialState = awaitItem() + assertThat(initialState).isEqualTo(RoomCallState.OnGoing(canJoinCall = false)) + } + } + + private fun createRoomCallStatePresenter( + matrixRoom: MatrixRoom + ): RoomCallStatePresenter { + return RoomCallStatePresenter( + room = matrixRoom, + ) + } +} diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts index 42f27963f3..231161e583 100644 --- a/features/roomdetails/impl/build.gradle.kts +++ b/features/roomdetails/impl/build.gradle.kts @@ -49,6 +49,7 @@ dependencies { implementation(projects.services.analytics.compose) implementation(projects.features.poll.api) implementation(projects.features.messages.api) + implementation(projects.features.roomcall.api) testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt index eccc8dd9d2..586790b618 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt @@ -21,6 +21,7 @@ import im.vector.app.features.analytics.plan.Interaction import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.messages.api.pinned.IsPinnedMessagesFeatureEnabled +import io.element.android.features.roomcall.api.RoomCallState import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.bool.orFalse @@ -37,7 +38,6 @@ import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.powerlevels.canInvite import io.element.android.libraries.matrix.api.room.powerlevels.canSendState import io.element.android.libraries.matrix.api.room.roomNotificationSettings -import io.element.android.libraries.matrix.ui.room.canCall import io.element.android.libraries.matrix.ui.room.getCurrentRoomMember import io.element.android.libraries.matrix.ui.room.getDirectRoomMember import io.element.android.libraries.matrix.ui.room.isOwnUserAdmin @@ -57,6 +57,7 @@ class RoomDetailsPresenter @Inject constructor( private val notificationSettingsService: NotificationSettingsService, private val roomMembersDetailsPresenterFactory: RoomMemberDetailsPresenter.Factory, private val leaveRoomPresenter: Presenter, + private val roomCallStatePresenter: Presenter, private val dispatchers: CoroutineDispatchers, private val analyticsService: AnalyticsService, private val isPinnedMessagesFeatureEnabled: IsPinnedMessagesFeatureEnabled, @@ -87,18 +88,16 @@ class RoomDetailsPresenter @Inject constructor( } } - val syncUpdateTimestamp by room.syncUpdateFlow.collectAsState() - val membersState by room.membersStateFlow.collectAsState() val canInvite by getCanInvite(membersState) val canEditName by getCanSendState(membersState, StateEventType.ROOM_NAME) val canEditAvatar by getCanSendState(membersState, StateEventType.ROOM_AVATAR) val canEditTopic by getCanSendState(membersState, StateEventType.ROOM_TOPIC) - val canJoinCall by room.canCall(updateKey = syncUpdateTimestamp) val dmMember by room.getDirectRoomMember(membersState) val currentMember by room.getCurrentRoomMember(membersState) val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember) val roomType by getRoomType(dmMember, currentMember) + val roomCallState = roomCallStatePresenter.present() val topicState = remember(canEditTopic, roomTopic, roomType) { val topic = roomTopic @@ -143,7 +142,7 @@ class RoomDetailsPresenter @Inject constructor( canInvite = canInvite, canEdit = (canEditAvatar || canEditName || canEditTopic) && roomType == RoomDetailsType.Room, canShowNotificationSettings = canShowNotificationSettings.value, - canCall = canJoinCall, + roomCallState = roomCallState, roomType = roomType, roomMemberDetailsState = roomMemberDetailsState, leaveRoomState = leaveRoomState, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt index 2208748563..d43b0a813a 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt @@ -9,6 +9,7 @@ package io.element.android.features.roomdetails.impl import androidx.compose.runtime.Immutable import io.element.android.features.leaveroom.api.LeaveRoomState +import io.element.android.features.roomcall.api.RoomCallState import io.element.android.features.userprofile.api.UserProfileState import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId @@ -31,7 +32,7 @@ data class RoomDetailsState( val canEdit: Boolean, val canInvite: Boolean, val canShowNotificationSettings: Boolean, - val canCall: Boolean, + val roomCallState: RoomCallState, val leaveRoomState: LeaveRoomState, val roomNotificationSettings: RoomNotificationSettings?, val isFavorite: Boolean, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt index 7e71d2b39f..49b9f73cb5 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt @@ -10,6 +10,8 @@ package io.element.android.features.roomdetails.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.leaveroom.api.aLeaveRoomState +import io.element.android.features.roomcall.api.RoomCallState +import io.element.android.features.roomcall.api.aStandByCallState import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.shared.aUserProfileState @@ -42,7 +44,7 @@ open class RoomDetailsStateProvider : PreviewParameterProvider // Also test the roomNotificationSettings ALL_MESSAGES in the same screenshot. Icon 'Mute' should be displayed roomNotificationSettings = aRoomNotificationSettings(mode = RoomNotificationMode.ALL_MESSAGES, isDefault = true) ), - aRoomDetailsState(canCall = false, canInvite = false), + aRoomDetailsState(roomCallState = aStandByCallState(false), canInvite = false), aRoomDetailsState(isPublic = false), aRoomDetailsState(heroes = aMatrixUserList()), aRoomDetailsState(pinnedMessagesCount = 3), @@ -89,7 +91,7 @@ fun aRoomDetailsState( canInvite: Boolean = false, canEdit: Boolean = false, canShowNotificationSettings: Boolean = true, - canCall: Boolean = true, + roomCallState: RoomCallState = aStandByCallState(), roomType: RoomDetailsType = RoomDetailsType.Room, roomMemberDetailsState: UserProfileState? = null, leaveRoomState: LeaveRoomState = aLeaveRoomState(), @@ -112,7 +114,7 @@ fun aRoomDetailsState( canInvite = canInvite, canEdit = canEdit, canShowNotificationSettings = canShowNotificationSettings, - canCall = canCall, + roomCallState = roomCallState, roomType = roomType, roomMemberDetailsState = roomMemberDetailsState, leaveRoomState = leaveRoomState, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index 163a2c5fd5..7e89c3ef07 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -42,6 +42,7 @@ import im.vector.app.features.analytics.plan.Interaction import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.leaveroom.api.LeaveRoomView +import io.element.android.features.roomcall.api.hasPermissionToJoin import io.element.android.features.userprofile.shared.blockuser.BlockUserDialogs import io.element.android.features.userprofile.shared.blockuser.BlockUserSection import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage @@ -299,7 +300,8 @@ private fun MainActionsSection( ) } } - if (state.canCall) { + if (state.roomCallState.hasPermissionToJoin()) { + // TODO Improve the view depending on all the cases here? MainActionButton( title = stringResource(CommonStrings.action_call), imageVector = CompoundIcons.VideoCall(), diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt index ea83f89181..b41090b661 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt @@ -17,6 +17,7 @@ import im.vector.app.features.analytics.plan.Interaction import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.leaveroom.api.aLeaveRoomState +import io.element.android.features.roomcall.api.aStandByCallState import io.element.android.features.roomdetails.aMatrixRoom import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter @@ -98,6 +99,7 @@ class RoomDetailsPresenterTest { notificationSettingsService = matrixClient.notificationSettingsService(), roomMembersDetailsPresenterFactory = roomMemberDetailsPresenterFactory, leaveRoomPresenter = { leaveRoomState }, + roomCallStatePresenter = { aStandByCallState() }, dispatchers = dispatchers, isPinnedMessagesFeatureEnabled = { isPinnedMessagesFeatureEnabled }, analyticsService = analyticsService, From 1c78f96148b2e5d737979db00275e027e8e5411a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Nov 2024 09:43:38 +0100 Subject: [PATCH 23/50] Ensure the user can join the call even if they has joined a call in another session. --- .../android/features/call/api/CurrentCall.kt | 25 ++++++++++++++++ .../features/call/api/CurrentCallObserver.kt | 19 ++++++++++++ .../call/impl/utils/ActiveCallManager.kt | 25 ++++++++++++++++ .../impl/utils/DefaultCurrentCallObserver.kt | 30 +++++++++++++++++++ .../utils/DefaultActiveCallManagerTest.kt | 2 ++ .../impl/timeline/components/CallMenuItem.kt | 2 +- .../features/roomcall/api/RoomCallState.kt | 1 + .../roomcall/api/RoomCallStateProvider.kt | 2 ++ features/roomcall/impl/build.gradle.kts | 1 + .../roomcall/impl/RoomCallStatePresenter.kt | 10 +++++++ 10 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCall.kt create mode 100644 features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallObserver.kt create mode 100644 features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCurrentCallObserver.kt diff --git a/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCall.kt b/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCall.kt new file mode 100644 index 0000000000..387d4a98ad --- /dev/null +++ b/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCall.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.call.api + +import io.element.android.libraries.matrix.api.core.RoomId + +/** + * Value for the local current call. + */ +sealed interface CurrentCall { + data object None : CurrentCall + + data class RoomCall( + val roomId: RoomId, + ) : CurrentCall + + data class ExternalUrl( + val url: String, + ) : CurrentCall +} diff --git a/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallObserver.kt b/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallObserver.kt new file mode 100644 index 0000000000..c0f8eb35a8 --- /dev/null +++ b/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallObserver.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.call.api + +import kotlinx.coroutines.flow.StateFlow + +interface CurrentCallObserver { + /** + * The current call state flow, which will be updated when the active call changes. + * This value reflect the local state of the call. It is not updated if the user answers + * a call from another session. + */ + val currentCall: StateFlow +} diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt index 45f1f2be63..ccdbc1b91a 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt @@ -12,6 +12,7 @@ import androidx.core.app.NotificationManagerCompat import com.squareup.anvil.annotations.ContributesBinding import io.element.android.appconfig.ElementCallConfig import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CurrentCall import io.element.android.features.call.impl.notifications.CallNotificationData import io.element.android.features.call.impl.notifications.RingingCallNotificationCreator import io.element.android.libraries.di.AppScope @@ -82,6 +83,7 @@ class DefaultActiveCallManager @Inject constructor( private val ringingCallNotificationCreator: RingingCallNotificationCreator, private val notificationManagerCompat: NotificationManagerCompat, private val matrixClientProvider: MatrixClientProvider, + private val defaultCurrentCallObserver: DefaultCurrentCallObserver, ) : ActiveCallManager { private var timedOutCallJob: Job? = null @@ -89,6 +91,7 @@ class DefaultActiveCallManager @Inject constructor( init { observeRingingCall() + observeCurrentCall() } override fun registerIncomingCall(notificationData: CallNotificationData) { @@ -209,6 +212,28 @@ class DefaultActiveCallManager @Inject constructor( } .launchIn(coroutineScope) } + + private fun observeCurrentCall() { + activeCall + .onEach { value -> + if (value == null) { + defaultCurrentCallObserver.onCallEnded() + } else { + when (value.callState) { + is CallState.Ringing -> { + // Nothing to do + } + is CallState.InCall -> { + when (val callType = value.callType) { + is CallType.ExternalUrl -> defaultCurrentCallObserver.onCallStarted(CurrentCall.ExternalUrl(callType.url)) + is CallType.RoomCall -> defaultCurrentCallObserver.onCallStarted(CurrentCall.RoomCall(callType.roomId)) + } + } + } + } + } + .launchIn(coroutineScope) + } } /** diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCurrentCallObserver.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCurrentCallObserver.kt new file mode 100644 index 0000000000..4810d31ee6 --- /dev/null +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCurrentCallObserver.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.call.impl.utils + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.call.api.CurrentCall +import io.element.android.features.call.api.CurrentCallObserver +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.SingleIn +import kotlinx.coroutines.flow.MutableStateFlow +import javax.inject.Inject + +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) +class DefaultCurrentCallObserver @Inject constructor() : CurrentCallObserver { + override val currentCall = MutableStateFlow(CurrentCall.None) + + fun onCallStarted(call: CurrentCall) { + currentCall.value = call + } + + fun onCallEnded() { + currentCall.value = CurrentCall.None + } +} diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt index 45568f2d39..93144ccd50 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt @@ -15,6 +15,7 @@ import io.element.android.features.call.impl.notifications.RingingCallNotificati import io.element.android.features.call.impl.utils.ActiveCall import io.element.android.features.call.impl.utils.CallState import io.element.android.features.call.impl.utils.DefaultActiveCallManager +import io.element.android.features.call.impl.utils.DefaultCurrentCallObserver import io.element.android.features.call.test.aCallNotificationData import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId @@ -299,5 +300,6 @@ class DefaultActiveCallManagerTest { ), notificationManagerCompat = notificationManagerCompat, matrixClientProvider = matrixClientProvider, + defaultCurrentCallObserver = DefaultCurrentCallObserver(), ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/CallMenuItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/CallMenuItem.kt index 58a3940475..2284ffa50c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/CallMenuItem.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/CallMenuItem.kt @@ -79,7 +79,7 @@ private fun OnGoingCallMenuItem( onJoinCallClick: () -> Unit, modifier: Modifier = Modifier, ) { - if (!roomCallState.isUserInTheCall) { + if (!roomCallState.isUserLocallyInTheCall) { Button( onClick = onJoinCallClick, colors = ButtonDefaults.buttonColors( diff --git a/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallState.kt b/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallState.kt index 77c58fee2c..e47a623914 100644 --- a/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallState.kt +++ b/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallState.kt @@ -20,6 +20,7 @@ sealed interface RoomCallState { data class OnGoing( val canJoinCall: Boolean, val isUserInTheCall: Boolean, + val isUserLocallyInTheCall: Boolean, ) : RoomCallState } diff --git a/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStateProvider.kt b/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStateProvider.kt index dce722c2c4..6351c25479 100644 --- a/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStateProvider.kt +++ b/features/roomcall/api/src/main/kotlin/io/element/android/features/roomcall/api/RoomCallStateProvider.kt @@ -22,9 +22,11 @@ open class RoomCallStateProvider : PreviewParameterProvider { fun anOngoingCallState( canJoinCall: Boolean = true, isUserInTheCall: Boolean = false, + isUserLocallyInTheCall: Boolean = isUserInTheCall, ) = RoomCallState.OnGoing( canJoinCall = canJoinCall, isUserInTheCall = isUserInTheCall, + isUserLocallyInTheCall = isUserLocallyInTheCall, ) fun aStandByCallState( diff --git a/features/roomcall/impl/build.gradle.kts b/features/roomcall/impl/build.gradle.kts index e8f65b160b..adb5b07bf2 100644 --- a/features/roomcall/impl/build.gradle.kts +++ b/features/roomcall/impl/build.gradle.kts @@ -20,6 +20,7 @@ setupAnvil() dependencies { api(projects.features.roomcall.api) implementation(libs.kotlinx.collections.immutable) + implementation(projects.features.call.api) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) diff --git a/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt b/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt index 175592af08..b6eb10b342 100644 --- a/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt +++ b/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt @@ -12,6 +12,8 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import io.element.android.features.call.api.CurrentCall +import io.element.android.features.call.api.CurrentCallObserver import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.room.MatrixRoom @@ -20,6 +22,7 @@ import javax.inject.Inject class RoomCallStatePresenter @Inject constructor( private val room: MatrixRoom, + private val currentCallObserver: CurrentCallObserver, ) : Presenter { @Composable override fun present(): RoomCallState { @@ -31,10 +34,17 @@ class RoomCallStatePresenter @Inject constructor( room.sessionId in roomInfo?.activeRoomCallParticipants.orEmpty() } } + val currentCall by currentCallObserver.currentCall.collectAsState() + val isUserLocallyInTheCall by remember { + derivedStateOf { + (currentCall as? CurrentCall.RoomCall)?.roomId == room.roomId + } + } val callState = when { roomInfo?.hasRoomCall == true -> RoomCallState.OnGoing( canJoinCall = canJoinCall, isUserInTheCall = isUserInTheCall, + isUserLocallyInTheCall = isUserLocallyInTheCall, ) else -> RoomCallState.StandBy(canStartCall = canJoinCall) } From 12e7172eb687908bd38c03bbf29480963f5bd83c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Nov 2024 10:11:50 +0100 Subject: [PATCH 24/50] Update tests --- .../call/test/FakeCurrentCallObserver.kt | 22 +++ .../impl/timeline/TimelinePresenterTest.kt | 2 + features/roomcall/impl/build.gradle.kts | 1 + .../impl/RoomCallStatePresenterTest.kt | 169 +++++++++++++++++- 4 files changed, 186 insertions(+), 8 deletions(-) create mode 100644 features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallObserver.kt diff --git a/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallObserver.kt b/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallObserver.kt new file mode 100644 index 0000000000..d153047c66 --- /dev/null +++ b/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallObserver.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.call.test + +import io.element.android.features.call.api.CurrentCall +import io.element.android.features.call.api.CurrentCallObserver +import kotlinx.coroutines.flow.MutableStateFlow + +class FakeCurrentCallObserver( + initialValue: CurrentCall = CurrentCall.None, +) : CurrentCallObserver { + override val currentCall = MutableStateFlow(initialValue) + + fun setCurrentCall(value: CurrentCall) { + currentCall.value = value + } +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt index 74df8ee46c..d153dd5743 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt @@ -27,6 +27,7 @@ import io.element.android.features.poll.api.actions.EndPollAction import io.element.android.features.poll.api.actions.SendPollResponseAction import io.element.android.features.poll.test.actions.FakeEndPollAction import io.element.android.features.poll.test.actions.FakeSendPollResponseAction +import io.element.android.features.roomcall.api.aStandByCallState import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState @@ -685,5 +686,6 @@ internal fun TestScope.createTimelinePresenter( timelineController = TimelineController(room), resolveVerifiedUserSendFailurePresenter = { aResolveVerifiedUserSendFailureState() }, typingNotificationPresenter = { aTypingNotificationState() }, + roomCallStatePresenter = { aStandByCallState() }, ) } diff --git a/features/roomcall/impl/build.gradle.kts b/features/roomcall/impl/build.gradle.kts index adb5b07bf2..6ac4ff934e 100644 --- a/features/roomcall/impl/build.gradle.kts +++ b/features/roomcall/impl/build.gradle.kts @@ -31,6 +31,7 @@ dependencies { testImplementation(libs.test.truth) testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.features.call.test) testImplementation(projects.tests.testutils) testImplementation(libs.androidx.compose.ui.test.junit) testReleaseImplementation(libs.androidx.compose.ui.test.manifest) diff --git a/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt b/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt index ae6b062738..aa4a0eb676 100644 --- a/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt +++ b/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt @@ -8,6 +8,9 @@ package io.element.android.features.roomcall.impl import com.google.common.truth.Truth.assertThat +import io.element.android.features.call.api.CurrentCall +import io.element.android.features.call.api.CurrentCallObserver +import io.element.android.features.call.test.FakeCurrentCallObserver import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom @@ -17,30 +20,180 @@ import kotlinx.coroutines.test.runTest import org.junit.Test class RoomCallStatePresenterTest { + @Test + fun `present - initial state`() = runTest { + val room = FakeMatrixRoom( + canUserJoinCallResult = { Result.success(false) }, + ) + val presenter = createRoomCallStatePresenter(matrixRoom = room) + presenter.test { + val initialState = awaitItem() + assertThat(initialState).isEqualTo( + RoomCallState.StandBy( + canStartCall = false, + ) + ) + } + } + + @Test + fun `present - initial state - user can join call`() = runTest { + val room = FakeMatrixRoom( + canUserJoinCallResult = { Result.success(true) }, + ) + val presenter = createRoomCallStatePresenter(matrixRoom = room) + presenter.test { + skipItems(1) + val initialState = awaitItem() + assertThat(initialState).isEqualTo( + RoomCallState.StandBy( + canStartCall = true, + ) + ) + } + } + @Test fun `present - call is disabled if user cannot join it even if there is an ongoing call`() = runTest { val room = FakeMatrixRoom( canUserJoinCallResult = { Result.success(false) }, - canUserSendMessageResult = { _, _ -> Result.success(true) }, - canRedactOwnResult = { Result.success(true) }, - canRedactOtherResult = { Result.success(true) }, - typingNoticeResult = { Result.success(Unit) }, - canUserPinUnpinResult = { Result.success(true) }, ).apply { givenRoomInfo(aRoomInfo(hasRoomCall = true)) } val presenter = createRoomCallStatePresenter(matrixRoom = room) presenter.test { - val initialState = awaitItem() - assertThat(initialState).isEqualTo(RoomCallState.OnGoing(canJoinCall = false)) + skipItems(1) + assertThat(awaitItem()).isEqualTo( + RoomCallState.OnGoing( + canJoinCall = false, + isUserInTheCall = false, + isUserLocallyInTheCall = false, + ) + ) + } + } + + @Test + fun `present - user has joined the call on another session`() = runTest { + val room = FakeMatrixRoom( + canUserJoinCallResult = { Result.success(true) }, + ).apply { + givenRoomInfo( + aRoomInfo( + hasRoomCall = true, + activeRoomCallParticipants = listOf(sessionId), + ) + ) + } + val presenter = createRoomCallStatePresenter(matrixRoom = room) + presenter.test { + skipItems(1) + assertThat(awaitItem()).isEqualTo( + RoomCallState.OnGoing( + canJoinCall = true, + isUserInTheCall = true, + isUserLocallyInTheCall = false, + ) + ) + } + } + + @Test + fun `present - user has joined the call locally`() = runTest { + val room = FakeMatrixRoom( + canUserJoinCallResult = { Result.success(true) }, + ).apply { + givenRoomInfo( + aRoomInfo( + hasRoomCall = true, + activeRoomCallParticipants = listOf(sessionId), + ) + ) + } + val presenter = createRoomCallStatePresenter( + matrixRoom = room, + currentCallObserver = FakeCurrentCallObserver(initialValue = CurrentCall.RoomCall(room.roomId)), + ) + presenter.test { + skipItems(1) + assertThat(awaitItem()).isEqualTo( + RoomCallState.OnGoing( + canJoinCall = true, + isUserInTheCall = true, + isUserLocallyInTheCall = true, + ) + ) + } + } + + @Test + fun `present - user leaves the call`() = runTest { + val room = FakeMatrixRoom( + canUserJoinCallResult = { Result.success(true) }, + ).apply { + givenRoomInfo( + aRoomInfo( + hasRoomCall = true, + activeRoomCallParticipants = listOf(sessionId), + ) + ) + } + val currentCallObserver = FakeCurrentCallObserver(initialValue = CurrentCall.RoomCall(room.roomId)) + val presenter = createRoomCallStatePresenter( + matrixRoom = room, + currentCallObserver = currentCallObserver + ) + presenter.test { + skipItems(1) + assertThat(awaitItem()).isEqualTo( + RoomCallState.OnGoing( + canJoinCall = true, + isUserInTheCall = true, + isUserLocallyInTheCall = true, + ) + ) + currentCallObserver.setCurrentCall(CurrentCall.None) + assertThat(awaitItem()).isEqualTo( + RoomCallState.OnGoing( + canJoinCall = true, + isUserInTheCall = true, + isUserLocallyInTheCall = false, + ) + ) + room.givenRoomInfo( + aRoomInfo( + hasRoomCall = true, + activeRoomCallParticipants = emptyList(), + ) + ) + assertThat(awaitItem()).isEqualTo( + RoomCallState.OnGoing( + canJoinCall = true, + isUserInTheCall = false, + isUserLocallyInTheCall = false, + ) + ) + room.givenRoomInfo( + aRoomInfo( + hasRoomCall = false, + activeRoomCallParticipants = emptyList(), + ) + ) + assertThat(awaitItem()).isEqualTo( + RoomCallState.StandBy( + canStartCall = true, + ) + ) } } private fun createRoomCallStatePresenter( - matrixRoom: MatrixRoom + matrixRoom: MatrixRoom, + currentCallObserver: CurrentCallObserver = FakeCurrentCallObserver(), ): RoomCallStatePresenter { return RoomCallStatePresenter( room = matrixRoom, + currentCallObserver = currentCallObserver, ) } } From 6f1de0cecc5e7e04ff4a80172bfce13523335227 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 6 Nov 2024 09:18:47 +0000 Subject: [PATCH 25/50] Update screenshots --- ....createroom.impl.components_RoomVisibilityOption_Day_0_en.png} | 0 ...reateroom.impl.components_RoomVisibilityOption_Night_0_en.png} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/uitests/src/test/snapshots/images/{features.createroom.impl.components_RoomPrivacyOption_Day_0_en.png => features.createroom.impl.components_RoomVisibilityOption_Day_0_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.createroom.impl.components_RoomPrivacyOption_Night_0_en.png => features.createroom.impl.components_RoomVisibilityOption_Night_0_en.png} (100%) diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomVisibilityOption_Day_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Day_0_en.png rename to tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomVisibilityOption_Day_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomVisibilityOption_Night_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomPrivacyOption_Night_0_en.png rename to tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomVisibilityOption_Night_0_en.png From 4c9662887fd8db4830de21808ab03c073de46fb3 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 6 Nov 2024 09:31:49 +0000 Subject: [PATCH 26/50] Update screenshots --- ...essages.impl.timeline.components_CallMenuItem_Day_0_en.png | 3 +++ ...essages.impl.timeline.components_CallMenuItem_Day_1_en.png | 3 +++ ...essages.impl.timeline.components_CallMenuItem_Day_2_en.png | 3 +++ ...essages.impl.timeline.components_CallMenuItem_Day_3_en.png | 3 +++ ...essages.impl.timeline.components_CallMenuItem_Day_4_en.png | 3 +++ ...sages.impl.timeline.components_CallMenuItem_Night_0_en.png | 3 +++ ...sages.impl.timeline.components_CallMenuItem_Night_1_en.png | 3 +++ ...sages.impl.timeline.components_CallMenuItem_Night_2_en.png | 3 +++ ...sages.impl.timeline.components_CallMenuItem_Night_3_en.png | 3 +++ ...sages.impl.timeline.components_CallMenuItem_Night_4_en.png | 3 +++ ...imeline.components_TimelineItemCallNotifyView_Day_0_en.png | 4 ++-- ...eline.components_TimelineItemCallNotifyView_Night_0_en.png | 4 ++-- 12 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_3_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_4_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_3_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_4_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_0_en.png new file mode 100644 index 0000000000..4b5528411e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a85dfe911ca378a94b3a8fab85efab4b0915d330e804ded3472c6e6a101a9f8 +size 4016 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_1_en.png new file mode 100644 index 0000000000..09f9005925 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f135ec4c24292d06f853d0ca17b90b61a8fab144e72021862152966cd750ecf9 +size 3970 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_2_en.png new file mode 100644 index 0000000000..d5b37d48d6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b512a152991e335a54d75d83a9190c87e09aaff34aa05d57fea3c585767cb1c5 +size 5658 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_3_en.png new file mode 100644 index 0000000000..653139d523 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ca4e84ced7c4ce27b4ff5319387756a3990edd2a8861a3b998ec5ed8705ece7 +size 5381 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_4_en.png new file mode 100644 index 0000000000..1b6fb4bab8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Day_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96a867cb12498cbdc97957bee07855dfaa13602baddaf933aff2b666ef4c7650 +size 3642 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_0_en.png new file mode 100644 index 0000000000..29d29ebd5f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75256919486ff2345c7db724caf5ee3767bbfa1d4a757c3ade9fb83802a65923 +size 4056 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_1_en.png new file mode 100644 index 0000000000..4dcdf87172 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e124fbad6f6dd867353937772b0911ec2ce521344a4acb9af1d8ade941ca0d5 +size 3980 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_2_en.png new file mode 100644 index 0000000000..edf7311f7f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5777c1bc314b886fe1b195a06c27ed8144464c466fa63c9a7fd6f00d20f8ebf0 +size 5433 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_3_en.png new file mode 100644 index 0000000000..34f71876f3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63a67d21058f9a0a9077c0a8c774ab3f5aff9dcee2dbdd197b571682296ba598 +size 5314 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_4_en.png new file mode 100644 index 0000000000..d6fd8eeb70 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_CallMenuItem_Night_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5bb36ccd718f3fec5b04f1bc812dc7718b5ea7fa4619c8b031466297a8d016fd +size 3659 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en.png index 464ad64ddd..b5371b0448 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2246cd94deba78469805df1a8ba4aa36fafee4179a5d124eb555e7f9408442e -size 18644 +oid sha256:178eef1c52bac961c9f29bc8c4df63923c8441884b1c717c9e516b9621ff3230 +size 37944 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en.png index 5936fd898b..b37717cfc7 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:029ffeafb441ebc800ba531c927cfa73259a04e06a04724310a0440238442b0e -size 18515 +oid sha256:9445d05edb98b6c3bed2a2aa3354031b7e116bf7753f9aa85b4db388751154a3 +size 38154 From cd481d3ca98355773af07fad879b434a5f7d9609 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Nov 2024 15:32:52 +0100 Subject: [PATCH 27/50] Add a message in the notification for the caption. Fixes #2602 --- .../notifications/factories/NotificationCreator.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt index dc57b0fe7b..b2a7a62679 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt @@ -424,6 +424,17 @@ class DefaultNotificationCreator @Inject constructor( message.extras.putString(MESSAGE_EVENT_ID, event.eventId.value) } addMessage(message) + + // Add additional message for captions + if (event.imageUri != null && event.body != null) { + addMessage( + MessagingStyle.Message( + event.body, + event.timestamp, + senderPerson, + ) + ) + } } } } From 64b189c48a74d95122f6ffba2acc4ec2c0eb758f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Nov 2024 15:41:42 +0100 Subject: [PATCH 28/50] Notification with image: provide the mimetype if available. --- .../DefaultNotifiableEventResolver.kt | 14 ++++++++++++++ .../NotificationBroadcastReceiverHandler.kt | 1 + .../notifications/factories/NotificationCreator.kt | 2 +- .../notifications/model/NotifiableMessageEvent.kt | 3 ++- .../DefaultNotifiableEventResolverTest.kt | 3 +++ .../fixtures/NotifiableEventFixture.kt | 1 + 6 files changed, 22 insertions(+), 2 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt index 2fb6186bd4..97e80f4dbf 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt @@ -107,6 +107,7 @@ class DefaultNotifiableEventResolver @Inject constructor( senderDisambiguatedDisplayName = senderDisambiguatedDisplayName, body = messageBody, imageUriString = content.fetchImageIfPresent(client)?.toString(), + imageMimeType = content.getImageMimetype(), roomName = roomDisplayName, roomIsDm = isDm, roomAvatarPath = roomAvatarUrl, @@ -316,6 +317,17 @@ class DefaultNotifiableEventResolver @Inject constructor( } .getOrNull() } + + private suspend fun NotificationContent.MessageLike.RoomMessage.getImageMimetype(): String? { + if (appPreferencesStore.doesHideImagesAndVideosFlow().first()) { + return null + } + return when (val messageType = messageType) { + is ImageMessageType -> messageType.info?.mimetype + is VideoMessageType -> null // Use the thumbnail here? + else -> null + } + } } @Suppress("LongParameterList") @@ -333,6 +345,7 @@ internal fun buildNotifiableMessageEvent( // We cannot use Uri? type here, as that could trigger a // NotSerializableException when persisting this to storage imageUriString: String? = null, + imageMimeType: String? = null, threadId: ThreadId? = null, roomName: String? = null, roomIsDm: Boolean = false, @@ -358,6 +371,7 @@ internal fun buildNotifiableMessageEvent( senderDisambiguatedDisplayName = senderDisambiguatedDisplayName, body = body, imageUriString = imageUriString, + imageMimeType = imageMimeType, threadId = threadId, roomName = roomName, roomIsDm = roomIsDm, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt index 97c53e945b..a3d553ba91 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt @@ -150,6 +150,7 @@ class NotificationBroadcastReceiverHandler @Inject constructor( ?: stringProvider.getString(R.string.notification_sender_me), body = message, imageUriString = null, + imageMimeType = null, threadId = threadId, roomName = room.displayName, roomIsDm = room.isDm, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt index b2a7a62679..559322eaae 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt @@ -419,7 +419,7 @@ class DefaultNotificationCreator @Inject constructor( senderPerson ).also { message -> event.imageUri?.let { - message.setData("image/", it) + message.setData(event.imageMimeType ?: "image/", it) } message.extras.putString(MESSAGE_EVENT_ID, event.eventId.value) } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt index 4a4d2612ae..d51c7e09de 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt @@ -31,7 +31,8 @@ data class NotifiableMessageEvent( val body: String?, // We cannot use Uri? type here, as that could trigger a // NotSerializableException when persisting this to storage - val imageUriString: String?, + private val imageUriString: String?, + val imageMimeType: String?, val threadId: ThreadId?, val roomName: String?, val roomIsDm: Boolean = false, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt index de29fcf1f0..153ebe1ffc 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt @@ -590,6 +590,7 @@ class DefaultNotifiableEventResolverTest { senderDisambiguatedDisplayName = A_USER_NAME_2, body = "Call in progress (unsupported)", imageUriString = null, + imageMimeType = null, threadId = null, roomName = A_ROOM_NAME, roomAvatarPath = null, @@ -669,6 +670,7 @@ class DefaultNotifiableEventResolverTest { canBeReplaced = false, isRedacted = false, imageUriString = null, + imageMimeType = null, type = EventType.CALL_NOTIFY, ) ) @@ -704,6 +706,7 @@ class DefaultNotifiableEventResolverTest { canBeReplaced = false, isRedacted = false, imageUriString = null, + imageMimeType = null, type = EventType.CALL_NOTIFY, ) ) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt index faba6a6c65..438718582f 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt @@ -100,6 +100,7 @@ fun aNotifiableMessageEvent( canBeReplaced = false, isRedacted = isRedacted, imageUriString = null, + imageMimeType = null, roomAvatarPath = null, senderAvatarPath = null, soundName = null, From c04bd64ca4208c62ed7fc5e9f3e48e6cbe642e0d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Nov 2024 15:47:10 +0100 Subject: [PATCH 29/50] Fix wrong description. --- .../impl/timeline/components/event/TimelineItemVideoView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt index 675f9adfc6..a6432c79ad 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt @@ -70,7 +70,7 @@ fun TimelineItemVideoView( onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit, modifier: Modifier = Modifier, ) { - val description = stringResource(CommonStrings.common_image) + val description = stringResource(CommonStrings.common_video) Column( modifier = modifier.semantics { contentDescription = description } ) { From f00478d9f37b1b16a1e5997172e2820f4a072b21 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Nov 2024 15:47:49 +0100 Subject: [PATCH 30/50] Format file... --- .../timeline/components/event/TimelineItemVideoView.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt index a6432c79ad..849084ea1c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt @@ -76,8 +76,8 @@ fun TimelineItemVideoView( ) { val containerModifier = if (content.showCaption) { Modifier - .padding(top = 6.dp) - .clip(RoundedCornerShape(6.dp)) + .padding(top = 6.dp) + .clip(RoundedCornerShape(6.dp)) } else { Modifier } @@ -93,8 +93,8 @@ fun TimelineItemVideoView( var isLoaded by remember { mutableStateOf(false) } AsyncImage( modifier = Modifier - .fillMaxWidth() - .then(if (isLoaded) Modifier.background(Color.White) else Modifier), + .fillMaxWidth() + .then(if (isLoaded) Modifier.background(Color.White) else Modifier), model = MediaRequestData( source = content.thumbnailSource, kind = MediaRequestData.Kind.File( From 461c31420e145627441a8fe30d7c89625d4c5285 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Nov 2024 16:04:38 +0100 Subject: [PATCH 31/50] Improve last message formatter: add caption (or filename) --- .../impl/DefaultRoomLastMessageFormatter.kt | 16 ++++--- .../DefaultRoomLastMessageFormatterTest.kt | 44 ++++++++++++++----- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt index 6b43fc3607..4031915445 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt @@ -110,25 +110,27 @@ class DefaultRoomLastMessageFormatter @Inject constructor( messageType.toPlainText(permalinkParser) } is VideoMessageType -> { - sp.getString(CommonStrings.common_video) + messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_video)) } is ImageMessageType -> { - sp.getString(CommonStrings.common_image) + messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_image)) } is StickerMessageType -> { - sp.getString(CommonStrings.common_sticker) + messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_sticker)) } is LocationMessageType -> { sp.getString(CommonStrings.common_shared_location) } is FileMessageType -> { - sp.getString(CommonStrings.common_file) + messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_file)) } is AudioMessageType -> { - sp.getString(CommonStrings.common_audio) + messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_audio)) } is VoiceMessageType -> { - sp.getString(CommonStrings.common_voice_message) + // In this case, do not use bestDescription, because the filename is useless, only use the caption if available. + messageType.caption?.prefixWith(sp.getString(CommonStrings.common_voice_message)) + ?: sp.getString(CommonStrings.common_voice_message) } is OtherMessageType -> { messageType.body @@ -140,7 +142,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor( return message.prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing) } - private fun String.prefixIfNeeded( + private fun CharSequence.prefixIfNeeded( senderDisambiguatedDisplayName: String, isDmRoom: Boolean, isOutgoing: Boolean, diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt index efc748d10f..3c5038069a 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt @@ -208,32 +208,51 @@ class DefaultRoomLastMessageFormatterTest { // Verify results of DM mode for ((type, result) in resultsInDm) { + val string = result.toString() val expectedResult = when (type) { - is VideoMessageType -> "Video" - is AudioMessageType -> "Audio" + is VideoMessageType -> "Video: Shared body" + is AudioMessageType -> "Audio: Shared body" is VoiceMessageType -> "Voice message" - is ImageMessageType -> "Image" - is StickerMessageType -> "Sticker" - is FileMessageType -> "File" + is ImageMessageType -> "Image: Shared body" + is StickerMessageType -> "Sticker: Shared body" + is FileMessageType -> "File: Shared body" is LocationMessageType -> "Shared location" is EmoteMessageType -> "* $senderName ${type.body}" is TextMessageType, is NoticeMessageType, is OtherMessageType -> body } - assertWithMessage("$type was not properly handled for DM").that(result).isEqualTo(expectedResult) + val shouldCreateAnnotatedString = when (type) { + is VideoMessageType -> true + is AudioMessageType -> true + is VoiceMessageType -> false + is ImageMessageType -> true + is StickerMessageType -> true + is FileMessageType -> true + is LocationMessageType -> false + is EmoteMessageType -> false + is TextMessageType -> false + is NoticeMessageType -> false + is OtherMessageType -> false + } + if (shouldCreateAnnotatedString) { + assertWithMessage("$type doesn't produce an AnnotatedString") + .that(result) + .isInstanceOf(AnnotatedString::class.java) + } + assertWithMessage("$type was not properly handled for DM").that(string).isEqualTo(expectedResult) } // Verify results of Room mode for ((type, result) in resultsInRoom) { val string = result.toString() val expectedResult = when (type) { - is VideoMessageType -> "$expectedPrefix: Video" - is AudioMessageType -> "$expectedPrefix: Audio" + is VideoMessageType -> "$expectedPrefix: Video: Shared body" + is AudioMessageType -> "$expectedPrefix: Audio: Shared body" is VoiceMessageType -> "$expectedPrefix: Voice message" - is ImageMessageType -> "$expectedPrefix: Image" - is StickerMessageType -> "$expectedPrefix: Sticker" - is FileMessageType -> "$expectedPrefix: File" + is ImageMessageType -> "$expectedPrefix: Image: Shared body" + is StickerMessageType -> "$expectedPrefix: Sticker: Shared body" + is FileMessageType -> "$expectedPrefix: File: Shared body" is LocationMessageType -> "$expectedPrefix: Shared location" is TextMessageType, is NoticeMessageType, @@ -249,7 +268,8 @@ class DefaultRoomLastMessageFormatterTest { is FileMessageType -> true is LocationMessageType -> false is EmoteMessageType -> false - is TextMessageType, is NoticeMessageType -> true + is TextMessageType -> true + is NoticeMessageType -> true is OtherMessageType -> true } if (shouldCreateAnnotatedString) { From f2c63a5a62da634e5b6d4c2d18d25c52ad46cc64 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Nov 2024 16:05:23 +0100 Subject: [PATCH 32/50] Do not render filename of voice message. --- .../impl/DefaultPinnedMessagesBannerFormatter.kt | 4 +++- .../impl/DefaultPinnedMessagesBannerFormatterTest.kt | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt index ab7a19a9c7..11873ee7c1 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt @@ -95,7 +95,9 @@ class DefaultPinnedMessagesBannerFormatter @Inject constructor( messageType.bestDescription.prefixWith(CommonStrings.common_audio) } is VoiceMessageType -> { - messageType.bestDescription.prefixWith(CommonStrings.common_voice_message) + // In this case, do not use bestDescription, because the filename is useless, only use the caption if available. + messageType.caption?.prefixWith(sp.getString(CommonStrings.common_voice_message)) + ?: sp.getString(CommonStrings.common_voice_message) } is OtherMessageType -> { messageType.body diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt index af347135fb..80a7691a1d 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt @@ -159,11 +159,11 @@ class DefaultPinnedMessagesBannerFormatterTest { val expectedResult = when (type) { is VideoMessageType, is AudioMessageType, - is VoiceMessageType, is ImageMessageType, is StickerMessageType, is FileMessageType, is LocationMessageType -> AnnotatedString::class.java + is VoiceMessageType, is EmoteMessageType, is TextMessageType, is NoticeMessageType, @@ -176,7 +176,7 @@ class DefaultPinnedMessagesBannerFormatterTest { val expectedResult = when (type) { is VideoMessageType -> "Video: Shared body" is AudioMessageType -> "Audio: Shared body" - is VoiceMessageType -> "Voice message: Shared body" + is VoiceMessageType -> "Voice message" is ImageMessageType -> "Image: Shared body" is StickerMessageType -> "Sticker: Shared body" is FileMessageType -> "File: Shared body" From 24d4270215d749d257ee7b34b08d6d19b27a1399 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 6 Nov 2024 14:57:55 +0100 Subject: [PATCH 33/50] create room : clean up after review. --- .../createroom/impl/CreateRoomDataStore.kt | 2 +- .../impl/components/RoomAccessOption.kt | 86 -------------- .../impl/components/RoomVisibilityOption.kt | 108 ------------------ .../configureroom/ConfigureRoomPresenter.kt | 2 +- .../impl/configureroom/ConfigureRoomView.kt | 41 ++++--- 5 files changed, 26 insertions(+), 213 deletions(-) delete mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomAccessOption.kt delete mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt index 36feb0aec3..56ebe326dd 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt @@ -37,7 +37,7 @@ class CreateRoomDataStore @Inject constructor( field = value } - val createRoomConfig: Flow = combine( + val createRoomConfigWithInvites: Flow = combine( selectedUserListDataStore.selectedUsers(), createRoomConfigFlow, ) { selectedUsers, config -> diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomAccessOption.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomAccessOption.kt deleted file mode 100644 index 6cde477f2e..0000000000 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomAccessOption.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only - * Please see LICENSE in the repository root for full details. - */ - -package io.element.android.features.createroom.impl.components - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.selection.selectable -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.Role -import androidx.compose.ui.unit.dp -import io.element.android.compound.theme.ElementTheme -import io.element.android.features.createroom.impl.configureroom.RoomAccessItem -import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.theme.components.RadioButton -import io.element.android.libraries.designsystem.theme.components.Text - -@Composable -fun RoomAccessOption( - roomAccessItem: RoomAccessItem, - onOptionClick: (RoomAccessItem) -> Unit, - modifier: Modifier = Modifier, - isSelected: Boolean = false, -) { - Row( - modifier - .fillMaxWidth() - .selectable( - selected = isSelected, - onClick = { onOptionClick(roomAccessItem) }, - role = Role.RadioButton, - ) - ) { - Column(Modifier.weight(1f)) { - Text( - text = stringResource(roomAccessItem.title), - style = ElementTheme.typography.fontBodyLgRegular, - color = MaterialTheme.colorScheme.primary, - ) - Spacer(Modifier.size(8.dp)) - Text( - text = stringResource(roomAccessItem.description), - style = ElementTheme.typography.fontBodySmRegular, - color = MaterialTheme.colorScheme.tertiary, - ) - } - RadioButton( - modifier = Modifier - .align(Alignment.CenterVertically) - .size(48.dp), - selected = isSelected, - // null recommended for accessibility with screenreaders - onClick = null - ) - } -} - -@PreviewsDayNight -@Composable -internal fun RoomAccessOptionPreview() = ElementPreview { - val aRoomAccessItem = RoomAccessItem.Anyone - Column { - RoomAccessOption( - roomAccessItem = aRoomAccessItem, - onOptionClick = {}, - isSelected = true, - ) - RoomAccessOption( - roomAccessItem = aRoomAccessItem, - onOptionClick = {}, - isSelected = false, - ) - } -} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt deleted file mode 100644 index e76296fa4c..0000000000 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2023, 2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only - * Please see LICENSE in the repository root for full details. - */ - -package io.element.android.features.createroom.impl.components - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.selection.selectable -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.Role -import androidx.compose.ui.unit.dp -import io.element.android.compound.theme.ElementTheme -import io.element.android.features.createroom.impl.configureroom.RoomVisibilityItem -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 -import io.element.android.libraries.designsystem.theme.components.RadioButton -import io.element.android.libraries.designsystem.theme.components.Text - -@Composable -fun RoomVisibilityOption( - roomPrivacyItem: RoomVisibilityItem, - onOptionClick: (RoomVisibilityItem) -> Unit, - modifier: Modifier = Modifier, - isSelected: Boolean = false, -) { - Row( - modifier - .fillMaxWidth() - .selectable( - selected = isSelected, - onClick = { onOptionClick(roomPrivacyItem) }, - role = Role.RadioButton, - ) - ) { - Box( - modifier = Modifier - .size(30.dp) - .clip(RoundedCornerShape(8.dp)) - .background(ElementTheme.colors.bgSubtleSecondary) - .padding(3.dp), - contentAlignment = Alignment.Center, - ) { - Icon( - resourceId = roomPrivacyItem.icon, - contentDescription = null, - tint = if (isSelected) ElementTheme.colors.iconPrimary else ElementTheme.colors.iconSecondary, - ) - } - Spacer(Modifier.size(16.dp)) - Column(Modifier.weight(1f)) { - Text( - text = stringResource(roomPrivacyItem.title), - style = ElementTheme.typography.fontBodyLgRegular, - color = MaterialTheme.colorScheme.primary, - ) - Spacer(Modifier.size(3.dp)) - Text( - text = stringResource(roomPrivacyItem.description), - style = ElementTheme.typography.fontBodySmRegular, - color = MaterialTheme.colorScheme.tertiary, - ) - } - - RadioButton( - modifier = Modifier - .align(Alignment.CenterVertically) - .size(48.dp), - selected = isSelected, - // null recommended for accessibility with screenreaders - onClick = null - ) - } -} - -@PreviewsDayNight -@Composable -internal fun RoomVisibilityOptionPreview() = ElementPreview { - val aRoomPrivacyItem = RoomVisibilityItem.Private - Column { - RoomVisibilityOption( - roomPrivacyItem = aRoomPrivacyItem, - onOptionClick = {}, - isSelected = true, - ) - RoomVisibilityOption( - roomPrivacyItem = aRoomPrivacyItem, - onOptionClick = {}, - isSelected = false, - ) - } -} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index 1b7623adfb..ad19d2cb9b 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -58,7 +58,7 @@ class ConfigureRoomPresenter @Inject constructor( @Composable override fun present(): ConfigureRoomState { val cameraPermissionState = cameraPermissionPresenter.present() - val createRoomConfig = dataStore.createRoomConfig.collectAsState(CreateRoomConfig()) + val createRoomConfig = dataStore.createRoomConfigWithInvites.collectAsState(CreateRoomConfig()) val homeserverName = remember { matrixClient.userIdServerName() } val isKnockFeatureEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock).collectAsState(initial = false) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index 1101f9d35f..b0084f68f6 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -36,16 +36,18 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.features.createroom.impl.R -import io.element.android.features.createroom.impl.components.RoomAccessOption -import io.element.android.features.createroom.impl.components.RoomVisibilityOption +import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom +import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize import io.element.android.libraries.designsystem.components.LabelledTextField import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.modifiers.clearFocusOnTap import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.aliasScreenTitle +import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton @@ -247,20 +249,17 @@ private fun RoomTopic( @Composable private fun ConfigureRoomOptions( title: String, - verticalArrangement: Arrangement.Vertical, modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit, ) { Column( - modifier = modifier - .selectableGroup() - .padding(horizontal = 12.dp), - verticalArrangement = verticalArrangement, + modifier = modifier.selectableGroup() ) { Text( text = title, style = ElementTheme.typography.fontBodyLgMedium, color = ElementTheme.colors.textPrimary, + modifier = Modifier.padding(horizontal = 16.dp), ) content() } @@ -275,13 +274,21 @@ private fun RoomVisibilityOptions( ConfigureRoomOptions( title = stringResource(R.string.screen_create_room_room_visibility_section_title), modifier = modifier, - verticalArrangement = Arrangement.spacedBy(16.dp), ) { RoomVisibilityItem.entries.forEach { item -> - RoomVisibilityOption( - roomPrivacyItem = item, - isSelected = item == selected, - onOptionClick = onOptionClick, + val isSelected = item == selected + ListItem( + leadingContent = ListItemContent.Custom { + RoundedIconAtom( + size = RoundedIconAtomSize.Big, + resourceId = item.icon, + tint = if (isSelected) ElementTheme.colors.iconPrimary else ElementTheme.colors.iconSecondary, + ) + }, + headlineContent = { Text(text = stringResource(item.title)) }, + supportingContent = { Text(text = stringResource(item.description)) }, + trailingContent = ListItemContent.RadioButton(selected = isSelected), + onClick = { onOptionClick(item) }, ) } } @@ -296,13 +303,13 @@ private fun RoomAccessOptions( ConfigureRoomOptions( title = stringResource(R.string.screen_create_room_room_access_section_header), modifier = modifier, - verticalArrangement = Arrangement.spacedBy(12.dp), ) { RoomAccessItem.entries.forEach { item -> - RoomAccessOption( - roomAccessItem = item, - isSelected = item == selected, - onOptionClick = onOptionClick, + ListItem( + headlineContent = { Text(text = stringResource(item.title)) }, + supportingContent = { Text(text = stringResource(item.description)) }, + trailingContent = ListItemContent.RadioButton(selected = item == selected), + onClick = { onOptionClick(item) }, ) } } From 2ab6289b893b23835338aca1214d961b6ce74a03 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 6 Nov 2024 14:09:08 +0000 Subject: [PATCH 34/50] Update screenshots --- ...s.createroom.impl.components_RoomAccessOption_Day_0_en.png | 3 --- ...createroom.impl.components_RoomAccessOption_Night_0_en.png | 3 --- ...eateroom.impl.components_RoomVisibilityOption_Day_0_en.png | 3 --- ...teroom.impl.components_RoomVisibilityOption_Night_0_en.png | 3 --- ...eateroom.impl.configureroom_ConfigureRoomView_Day_0_en.png | 4 ++-- ...eateroom.impl.configureroom_ConfigureRoomView_Day_1_en.png | 4 ++-- ...eateroom.impl.configureroom_ConfigureRoomView_Day_2_en.png | 4 ++-- ...teroom.impl.configureroom_ConfigureRoomView_Night_0_en.png | 4 ++-- ...teroom.impl.configureroom_ConfigureRoomView_Night_1_en.png | 4 ++-- ...teroom.impl.configureroom_ConfigureRoomView_Night_2_en.png | 4 ++-- 10 files changed, 12 insertions(+), 24 deletions(-) delete mode 100644 tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomAccessOption_Day_0_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomAccessOption_Night_0_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomVisibilityOption_Day_0_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomVisibilityOption_Night_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomAccessOption_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomAccessOption_Day_0_en.png deleted file mode 100644 index a45ad2fd47..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomAccessOption_Day_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:39e248b3ba5e1c1b41d557471826f1d67d9c1d71b6b8ec34ca6226358dd396b5 -size 15609 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomAccessOption_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomAccessOption_Night_0_en.png deleted file mode 100644 index a5b7c14008..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomAccessOption_Night_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:81760d5d90b82dee085f30b6c20e259caaafc63a68033137ae28d1b2d8f2f121 -size 15125 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomVisibilityOption_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomVisibilityOption_Day_0_en.png deleted file mode 100644 index 453d07fd59..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomVisibilityOption_Day_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:43e7348faba79c517dc05d92f8bbc40a6bee7d977f8667ee6e945a7e722b20b5 -size 30728 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomVisibilityOption_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomVisibilityOption_Night_0_en.png deleted file mode 100644 index 6e18ae0790..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.components_RoomVisibilityOption_Night_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aec1f68cd88c087e22fae3f1334c6d7afaa085c71c8f121b4d653bca3bcfa8a6 -size 30074 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en.png index 155986b58b..28f4bd5c7d 100644 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:901462c6c5f40e95db357ad964aff83179e36182e0ec61b832fe73c76697257a -size 53949 +oid sha256:e7de44cc8f686264788aa79cde24b9896c6ca4dabc49b246abbd1d3dd0311e25 +size 57891 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en.png index 871fa11194..3131e96896 100644 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db6b448c513d3f173a988f1151a82af8455b00d822f75b76103067260ae1bac7 -size 76200 +oid sha256:2df49a9c82438dbf1ba0ba0670cadd8e7bb47a7e25de27793fd756760cf7c5f2 +size 79562 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_2_en.png index 12208bc0e9..3131e96896 100644 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a2b4959fc4ca3a0b363b08e3724d0ad957f588320a69f901faaeb36d913b60f -size 76229 +oid sha256:2df49a9c82438dbf1ba0ba0670cadd8e7bb47a7e25de27793fd756760cf7c5f2 +size 79562 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en.png index e349d30b1e..a1a2247eb8 100644 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90cd88d155bf54dc233d95c27c43d4ede4eccfd175840f592ff4882d3b2cb261 -size 52329 +oid sha256:c754daf307554dc0c83ba0f89c38f5fa7f5c2edff82907b0826c2b781498dbcb +size 56047 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en.png index f7483d3eb3..2a7721de80 100644 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0601ca5a9c3183e8244b5eaf1a14cf052afe15c61cd69a74472b9db4062caa3f -size 75345 +oid sha256:b6e43161abdccacbede4a8c4b4ff446026035df4be0894a55c6364681458bb2f +size 78805 diff --git a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_2_en.png index 86368097a9..2a7721de80 100644 --- a/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.createroom.impl.configureroom_ConfigureRoomView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bcb08e5d84df7a89e4851d631f1ad7b2bce6213cfd9be37d98782d1d778d0b23 -size 75375 +oid sha256:b6e43161abdccacbede4a8c4b4ff446026035df4be0894a55c6364681458bb2f +size 78805 From cc4a8d47ebcca14e966426b507e4114806e84ad4 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 6 Nov 2024 15:31:22 +0100 Subject: [PATCH 35/50] rust sdk : handle api breaks for 0.2.59 --- .../impl/RoomAliasResolverPresenter.kt | 5 ++++- .../impl/RoomAliasResolverPresenterTest.kt | 5 +++-- .../impl/root/RoomDirectoryPresenter.kt | 4 +++- .../impl/root/RoomDirectoryPresenterTest.kt | 4 ++-- .../android/libraries/matrix/api/MatrixClient.kt | 9 ++++++++- .../api/roomdirectory/RoomDirectoryList.kt | 16 +++++++++++++++- .../libraries/matrix/impl/RustMatrixClient.kt | 16 ++++++++-------- .../impl/roomdirectory/RustRoomDirectoryList.kt | 4 ++-- .../fakes/FakeRustRoomDirectorySearch.kt | 2 +- .../roomdirectory/RustRoomDirectoryListTest.kt | 2 +- .../libraries/matrix/test/FakeMatrixClient.kt | 8 ++++++-- .../test/roomdirectory/FakeRoomDirectoryList.kt | 4 ++-- 12 files changed, 55 insertions(+), 24 deletions(-) diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt index abb6682a66..ffe98680dd 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import kotlin.jvm.optionals.getOrElse class RoomAliasResolverPresenter @AssistedInject constructor( @Assisted private val roomAlias: RoomAlias, @@ -57,7 +58,9 @@ class RoomAliasResolverPresenter @AssistedInject constructor( private fun CoroutineScope.resolveAlias(resolveState: MutableState>) = launch { suspend { - matrixClient.resolveRoomAlias(roomAlias).getOrThrow() + matrixClient.resolveRoomAlias(roomAlias) + .getOrThrow() + .getOrElse { error("Failed to resolve room alias $roomAlias") } }.runCatchingUpdatingState(resolveState) } } diff --git a/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenterTest.kt b/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenterTest.kt index e651cf41e8..9894c2b342 100644 --- a/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenterTest.kt +++ b/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenterTest.kt @@ -24,6 +24,7 @@ import io.element.android.tests.testutils.WarmUpRule import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test +import java.util.Optional class RoomAliasResolverPresenterTest { @get:Rule @@ -42,7 +43,7 @@ class RoomAliasResolverPresenterTest { @Test fun `present - resolve alias to roomId`() = runTest { - val result = aResolvedRoomAlias() + val result = Optional.of(aResolvedRoomAlias()) val client = FakeMatrixClient( resolveRoomAliasResult = { Result.success(result) } ) @@ -54,7 +55,7 @@ class RoomAliasResolverPresenterTest { assertThat(awaitItem().resolveState.isLoading()).isTrue() val resultState = awaitItem() assertThat(resultState.roomAlias).isEqualTo(A_ROOM_ALIAS) - assertThat(resultState.resolveState.dataOrNull()).isEqualTo(result) + assertThat(resultState.resolveState.dataOrNull()).isEqualTo(result.get()) } } diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt index 2f3b0cff75..a5b62ff1e3 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt @@ -28,6 +28,8 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import javax.inject.Inject +private const val SEARCH_BATCH_SIZE = 20 + class RoomDirectoryPresenter @Inject constructor( private val dispatchers: CoroutineDispatchers, private val roomDirectoryService: RoomDirectoryService, @@ -51,7 +53,7 @@ class RoomDirectoryPresenter @Inject constructor( loadingMore = false // debounce search query delay(300) - roomDirectoryList.filter(searchQuery, 20) + roomDirectoryList.filter(filter = searchQuery, batchSize = SEARCH_BATCH_SIZE, viaServerName = null) } LaunchedEffect(loadingMore) { if (loadingMore) { diff --git a/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenterTest.kt b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenterTest.kt index 10dda624bd..20a709bce7 100644 --- a/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenterTest.kt +++ b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenterTest.kt @@ -81,7 +81,7 @@ import org.junit.Test @Test fun `present - emit search event`() = runTest { - val filterLambda = lambdaRecorder { _: String?, _: Int -> + val filterLambda = lambdaRecorder { _: String?, _: Int, _: String? -> Result.success(Unit) } val roomDirectoryList = FakeRoomDirectoryList(filterLambda = filterLambda) @@ -99,7 +99,7 @@ import org.junit.Test } assert(filterLambda) .isCalledOnce() - .with(value("test"), any()) + .with(value("test"), any(), value(null)) } @Test diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index 6af1763e83..504db1c6d9 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -108,7 +108,14 @@ interface MatrixClient : Closeable { suspend fun trackRecentlyVisitedRoom(roomId: RoomId): Result suspend fun getRecentlyVisitedRooms(): Result> - suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result + + /** + * Resolves the given room alias to a roomID (and a list of servers), if possible. + * @param roomAlias the room alias to resolve + * @return the resolved room alias if any, an empty result if not found,or an error if the resolution failed. + * + */ + suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result> /** * Enables or disables the sending queue, according to the given parameter. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryList.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryList.kt index 5f362dfda5..88cabf265b 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryList.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryList.kt @@ -10,8 +10,22 @@ package io.element.android.libraries.matrix.api.roomdirectory import kotlinx.coroutines.flow.Flow interface RoomDirectoryList { - suspend fun filter(filter: String?, batchSize: Int): Result + /** + * Starts a filtered search for the server. + * If the filter is not provided it will search for all the rooms. You can specify a batch_size to control the number of rooms to fetch per request. + * If the via_server is not provided it will search in the current homeserver by default. + * This method will clear the current search results and start a new one + */ + suspend fun filter(filter: String?, batchSize: Int, viaServerName: String?): Result + + /** + * Load more rooms from the current search results. + */ suspend fun loadMore(): Result + + /** + * The current search results as a state flow. + */ val state: Flow data class State( diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 101d049659..375d704ef9 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -71,7 +71,6 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.Channel @@ -112,7 +111,6 @@ import org.matrix.rustcomponents.sdk.RoomPreset as RustRoomPreset import org.matrix.rustcomponents.sdk.RoomVisibility as RustRoomVisibility import org.matrix.rustcomponents.sdk.SyncService as ClientSyncService -@OptIn(ExperimentalCoroutinesApi::class) class RustMatrixClient( private val client: Client, private val baseDirectory: File, @@ -420,13 +418,15 @@ class RustMatrixClient( } } - override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result = withContext(sessionDispatcher) { + override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result> = withContext(sessionDispatcher) { runCatching { - val result = client.resolveRoomAlias(roomAlias.value) - ResolvedRoomAlias( - roomId = RoomId(result.roomId), - servers = result.servers, - ) + val result = client.resolveRoomAlias(roomAlias.value)?.let { + ResolvedRoomAlias( + roomId = RoomId(it.roomId), + servers = it.servers, + ) + } + Optional.ofNullable(result) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryList.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryList.kt index 7219e9c3ed..576b93d26d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryList.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryList.kt @@ -41,9 +41,9 @@ class RustRoomDirectoryList( .launchIn(coroutineScope) } - override suspend fun filter(filter: String?, batchSize: Int): Result { + override suspend fun filter(filter: String?, batchSize: Int, viaServerName: String?): Result { return execute { - inner.search(filter = filter, batchSize = batchSize.toUInt()) + inner.search(filter = filter, batchSize = batchSize.toUInt(), viaServerName = viaServerName) } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustRoomDirectorySearch.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustRoomDirectorySearch.kt index 0569ee55c8..42e078d1c8 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustRoomDirectorySearch.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustRoomDirectorySearch.kt @@ -21,7 +21,7 @@ class FakeRustRoomDirectorySearch( return isAtLastPage } - override suspend fun search(filter: String?, batchSize: UInt) = simulateLongTask { } + override suspend fun search(filter: String?, batchSize: UInt, viaServerName: String?) = simulateLongTask { } override suspend fun nextPage() = simulateLongTask { } private var listener: RoomDirectorySearchEntriesListener? = null diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryListTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryListTest.kt index 134b90adc9..0eca9edde5 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryListTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryListTest.kt @@ -36,7 +36,7 @@ class RustRoomDirectoryListTest { // Let the mxCallback be ready runCurrent() sut.state.test { - sut.filter("", 20) + sut.filter(filter = "", batchSize = 20, viaServerName = null) roomDirectorySearch.emitResult( listOf( RoomDirectorySearchEntryUpdate.Append(listOf(aRustRoomDescription())) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 2c69048c39..dd7869cdf7 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -73,7 +73,11 @@ class FakeMatrixClient( private val encryptionService: FakeEncryptionService = FakeEncryptionService(), private val roomDirectoryService: RoomDirectoryService = FakeRoomDirectoryService(), private val accountManagementUrlString: Result = Result.success(null), - private val resolveRoomAliasResult: (RoomAlias) -> Result = { Result.success(ResolvedRoomAlias(A_ROOM_ID, emptyList())) }, + private val resolveRoomAliasResult: (RoomAlias) -> Result> = { + Result.success( + Optional.of(ResolvedRoomAlias(A_ROOM_ID, emptyList())) + ) + }, private val getRoomPreviewResult: (RoomIdOrAlias, List) -> Result = { _, _ -> Result.failure(AN_EXCEPTION) }, private val clearCacheLambda: () -> Unit = { lambdaError() }, private val userIdServerNameLambda: () -> String = { lambdaError() }, @@ -305,7 +309,7 @@ class FakeMatrixClient( return Result.success(Unit) } - override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result = simulateLongTask { + override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result> = simulateLongTask { resolveRoomAliasResult(roomAlias) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryList.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryList.kt index afe1e52074..6a3fcb3b45 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryList.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryList.kt @@ -13,10 +13,10 @@ import kotlinx.coroutines.flow.emptyFlow class FakeRoomDirectoryList( override val state: Flow = emptyFlow(), - val filterLambda: (String?, Int) -> Result = { _, _ -> Result.success(Unit) }, + val filterLambda: (String?, Int, String?) -> Result = { _, _, _ -> Result.success(Unit) }, val loadMoreLambda: () -> Result = { Result.success(Unit) } ) : RoomDirectoryList { - override suspend fun filter(filter: String?, batchSize: Int) = filterLambda(filter, batchSize) + override suspend fun filter(filter: String?, batchSize: Int, viaServerName: String?): Result = filterLambda(filter, batchSize, viaServerName) override suspend fun loadMore(): Result = loadMoreLambda() } From 2b5acb30232d026fc71f9e7e00a3c228ce370cb3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Nov 2024 17:24:26 +0100 Subject: [PATCH 36/50] Rename CurrentCallObserver to CurrentCallService --- ...rentCallObserver.kt => CurrentCallService.kt} | 2 +- .../call/impl/utils/ActiveCallManager.kt | 8 ++++---- ...lObserver.kt => DefaultCurrentCallService.kt} | 4 ++-- .../call/utils/DefaultActiveCallManagerTest.kt | 4 ++-- ...CallObserver.kt => FakeCurrentCallService.kt} | 6 +++--- .../roomcall/impl/RoomCallStatePresenter.kt | 6 +++--- .../roomcall/impl/RoomCallStatePresenterTest.kt | 16 ++++++++-------- 7 files changed, 23 insertions(+), 23 deletions(-) rename features/call/api/src/main/kotlin/io/element/android/features/call/api/{CurrentCallObserver.kt => CurrentCallService.kt} (94%) rename features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/{DefaultCurrentCallObserver.kt => DefaultCurrentCallService.kt} (84%) rename features/call/test/src/main/kotlin/io/element/android/features/call/test/{FakeCurrentCallObserver.kt => FakeCurrentCallService.kt} (80%) diff --git a/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallObserver.kt b/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallService.kt similarity index 94% rename from features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallObserver.kt rename to features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallService.kt index c0f8eb35a8..9cc61ab96d 100644 --- a/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallObserver.kt +++ b/features/call/api/src/main/kotlin/io/element/android/features/call/api/CurrentCallService.kt @@ -9,7 +9,7 @@ package io.element.android.features.call.api import kotlinx.coroutines.flow.StateFlow -interface CurrentCallObserver { +interface CurrentCallService { /** * The current call state flow, which will be updated when the active call changes. * This value reflect the local state of the call. It is not updated if the user answers diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt index ccdbc1b91a..d774a5133c 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt @@ -83,7 +83,7 @@ class DefaultActiveCallManager @Inject constructor( private val ringingCallNotificationCreator: RingingCallNotificationCreator, private val notificationManagerCompat: NotificationManagerCompat, private val matrixClientProvider: MatrixClientProvider, - private val defaultCurrentCallObserver: DefaultCurrentCallObserver, + private val defaultCurrentCallService: DefaultCurrentCallService, ) : ActiveCallManager { private var timedOutCallJob: Job? = null @@ -217,7 +217,7 @@ class DefaultActiveCallManager @Inject constructor( activeCall .onEach { value -> if (value == null) { - defaultCurrentCallObserver.onCallEnded() + defaultCurrentCallService.onCallEnded() } else { when (value.callState) { is CallState.Ringing -> { @@ -225,8 +225,8 @@ class DefaultActiveCallManager @Inject constructor( } is CallState.InCall -> { when (val callType = value.callType) { - is CallType.ExternalUrl -> defaultCurrentCallObserver.onCallStarted(CurrentCall.ExternalUrl(callType.url)) - is CallType.RoomCall -> defaultCurrentCallObserver.onCallStarted(CurrentCall.RoomCall(callType.roomId)) + is CallType.ExternalUrl -> defaultCurrentCallService.onCallStarted(CurrentCall.ExternalUrl(callType.url)) + is CallType.RoomCall -> defaultCurrentCallService.onCallStarted(CurrentCall.RoomCall(callType.roomId)) } } } diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCurrentCallObserver.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCurrentCallService.kt similarity index 84% rename from features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCurrentCallObserver.kt rename to features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCurrentCallService.kt index 4810d31ee6..a2a358483b 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCurrentCallObserver.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCurrentCallService.kt @@ -9,7 +9,7 @@ package io.element.android.features.call.impl.utils import com.squareup.anvil.annotations.ContributesBinding import io.element.android.features.call.api.CurrentCall -import io.element.android.features.call.api.CurrentCallObserver +import io.element.android.features.call.api.CurrentCallService import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.SingleIn import kotlinx.coroutines.flow.MutableStateFlow @@ -17,7 +17,7 @@ import javax.inject.Inject @SingleIn(AppScope::class) @ContributesBinding(AppScope::class) -class DefaultCurrentCallObserver @Inject constructor() : CurrentCallObserver { +class DefaultCurrentCallService @Inject constructor() : CurrentCallService { override val currentCall = MutableStateFlow(CurrentCall.None) fun onCallStarted(call: CurrentCall) { diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt index 93144ccd50..b9f6a524ae 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt @@ -15,7 +15,7 @@ import io.element.android.features.call.impl.notifications.RingingCallNotificati import io.element.android.features.call.impl.utils.ActiveCall import io.element.android.features.call.impl.utils.CallState import io.element.android.features.call.impl.utils.DefaultActiveCallManager -import io.element.android.features.call.impl.utils.DefaultCurrentCallObserver +import io.element.android.features.call.impl.utils.DefaultCurrentCallService import io.element.android.features.call.test.aCallNotificationData import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId @@ -300,6 +300,6 @@ class DefaultActiveCallManagerTest { ), notificationManagerCompat = notificationManagerCompat, matrixClientProvider = matrixClientProvider, - defaultCurrentCallObserver = DefaultCurrentCallObserver(), + defaultCurrentCallService = DefaultCurrentCallService(), ) } diff --git a/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallObserver.kt b/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallService.kt similarity index 80% rename from features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallObserver.kt rename to features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallService.kt index d153047c66..ff0a9e9096 100644 --- a/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallObserver.kt +++ b/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallService.kt @@ -8,12 +8,12 @@ package io.element.android.features.call.test import io.element.android.features.call.api.CurrentCall -import io.element.android.features.call.api.CurrentCallObserver +import io.element.android.features.call.api.CurrentCallService import kotlinx.coroutines.flow.MutableStateFlow -class FakeCurrentCallObserver( +class FakeCurrentCallService( initialValue: CurrentCall = CurrentCall.None, -) : CurrentCallObserver { +) : CurrentCallService { override val currentCall = MutableStateFlow(initialValue) fun setCurrentCall(value: CurrentCall) { diff --git a/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt b/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt index b6eb10b342..57ac545c19 100644 --- a/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt +++ b/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt @@ -13,7 +13,7 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import io.element.android.features.call.api.CurrentCall -import io.element.android.features.call.api.CurrentCallObserver +import io.element.android.features.call.api.CurrentCallService import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.room.MatrixRoom @@ -22,7 +22,7 @@ import javax.inject.Inject class RoomCallStatePresenter @Inject constructor( private val room: MatrixRoom, - private val currentCallObserver: CurrentCallObserver, + private val currentCallService: CurrentCallService, ) : Presenter { @Composable override fun present(): RoomCallState { @@ -34,7 +34,7 @@ class RoomCallStatePresenter @Inject constructor( room.sessionId in roomInfo?.activeRoomCallParticipants.orEmpty() } } - val currentCall by currentCallObserver.currentCall.collectAsState() + val currentCall by currentCallService.currentCall.collectAsState() val isUserLocallyInTheCall by remember { derivedStateOf { (currentCall as? CurrentCall.RoomCall)?.roomId == room.roomId diff --git a/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt b/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt index aa4a0eb676..ba0b425ec5 100644 --- a/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt +++ b/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt @@ -9,8 +9,8 @@ package io.element.android.features.roomcall.impl import com.google.common.truth.Truth.assertThat import io.element.android.features.call.api.CurrentCall -import io.element.android.features.call.api.CurrentCallObserver -import io.element.android.features.call.test.FakeCurrentCallObserver +import io.element.android.features.call.api.CurrentCallService +import io.element.android.features.call.test.FakeCurrentCallService import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom @@ -112,7 +112,7 @@ class RoomCallStatePresenterTest { } val presenter = createRoomCallStatePresenter( matrixRoom = room, - currentCallObserver = FakeCurrentCallObserver(initialValue = CurrentCall.RoomCall(room.roomId)), + currentCallService = FakeCurrentCallService(initialValue = CurrentCall.RoomCall(room.roomId)), ) presenter.test { skipItems(1) @@ -138,10 +138,10 @@ class RoomCallStatePresenterTest { ) ) } - val currentCallObserver = FakeCurrentCallObserver(initialValue = CurrentCall.RoomCall(room.roomId)) + val currentCallService = FakeCurrentCallService(initialValue = CurrentCall.RoomCall(room.roomId)) val presenter = createRoomCallStatePresenter( matrixRoom = room, - currentCallObserver = currentCallObserver + currentCallService = currentCallService ) presenter.test { skipItems(1) @@ -152,7 +152,7 @@ class RoomCallStatePresenterTest { isUserLocallyInTheCall = true, ) ) - currentCallObserver.setCurrentCall(CurrentCall.None) + currentCallService.setCurrentCall(CurrentCall.None) assertThat(awaitItem()).isEqualTo( RoomCallState.OnGoing( canJoinCall = true, @@ -189,11 +189,11 @@ class RoomCallStatePresenterTest { private fun createRoomCallStatePresenter( matrixRoom: MatrixRoom, - currentCallObserver: CurrentCallObserver = FakeCurrentCallObserver(), + currentCallService: CurrentCallService = FakeCurrentCallService(), ): RoomCallStatePresenter { return RoomCallStatePresenter( room = matrixRoom, - currentCallObserver = currentCallObserver, + currentCallService = currentCallService, ) } } From db4b4d3fa4e3434e13a2c7af4ee7abb578304c5f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Nov 2024 17:29:20 +0100 Subject: [PATCH 37/50] Provide MutableStateFlow in the constructor of the fake class. --- .../features/call/test/FakeCurrentCallService.kt | 10 ++-------- .../roomcall/impl/RoomCallStatePresenterTest.kt | 8 +++++--- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallService.kt b/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallService.kt index ff0a9e9096..b9efc70ece 100644 --- a/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallService.kt +++ b/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeCurrentCallService.kt @@ -12,11 +12,5 @@ import io.element.android.features.call.api.CurrentCallService import kotlinx.coroutines.flow.MutableStateFlow class FakeCurrentCallService( - initialValue: CurrentCall = CurrentCall.None, -) : CurrentCallService { - override val currentCall = MutableStateFlow(initialValue) - - fun setCurrentCall(value: CurrentCall) { - currentCall.value = value - } -} + override val currentCall: MutableStateFlow = MutableStateFlow(CurrentCall.None), +) : CurrentCallService diff --git a/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt b/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt index ba0b425ec5..d29bff08ff 100644 --- a/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt +++ b/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt @@ -16,6 +16,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.tests.testutils.test +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.Test @@ -112,7 +113,7 @@ class RoomCallStatePresenterTest { } val presenter = createRoomCallStatePresenter( matrixRoom = room, - currentCallService = FakeCurrentCallService(initialValue = CurrentCall.RoomCall(room.roomId)), + currentCallService = FakeCurrentCallService(MutableStateFlow(CurrentCall.RoomCall(room.roomId))), ) presenter.test { skipItems(1) @@ -138,7 +139,8 @@ class RoomCallStatePresenterTest { ) ) } - val currentCallService = FakeCurrentCallService(initialValue = CurrentCall.RoomCall(room.roomId)) + val currentCall = MutableStateFlow(CurrentCall.RoomCall(room.roomId)) + val currentCallService = FakeCurrentCallService(currentCall = currentCall) val presenter = createRoomCallStatePresenter( matrixRoom = room, currentCallService = currentCallService @@ -152,7 +154,7 @@ class RoomCallStatePresenterTest { isUserLocallyInTheCall = true, ) ) - currentCallService.setCurrentCall(CurrentCall.None) + currentCall.value = CurrentCall.None assertThat(awaitItem()).isEqualTo( RoomCallState.OnGoing( canJoinCall = true, From d462386624aaf35818fc8816f7660955ac6eed9a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 Nov 2024 09:58:29 +0100 Subject: [PATCH 38/50] Remove code duplication - no behavior change. --- .../impl/outgoing/VerifySelfSessionView.kt | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt index fdcbf978a3..9bb59daa6f 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt @@ -248,25 +248,18 @@ private fun VerifySelfSessionBottomMenu( Step.Loading -> error("Should not happen") is Step.Initial -> { VerificationBottomMenu { - if (verificationViewState.isLastDevice) { - Button( - modifier = Modifier.fillMaxWidth(), - text = stringResource(R.string.screen_session_verification_enter_recovery_key), - onClick = onEnterRecoveryKey, - ) - } else { + if (verificationViewState.isLastDevice.not()) { Button( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.screen_identity_use_another_device), onClick = { eventSink(VerifySelfSessionViewEvents.RequestVerification) }, ) - Button( - modifier = Modifier.fillMaxWidth(), - text = stringResource(R.string.screen_session_verification_enter_recovery_key), - onClick = onEnterRecoveryKey, - ) } - // This option should always be displayed + Button( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.screen_session_verification_enter_recovery_key), + onClick = onEnterRecoveryKey, + ) OutlinedButton( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.screen_identity_confirmation_cannot_confirm), From 104358502d50bd2020eae9f60eacb6c67e1badd9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:42:09 +0100 Subject: [PATCH 39/50] Update mobile-dev-inc/action-maestro-cloud action to v1.9.4 (#3820) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/maestro.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maestro.yml b/.github/workflows/maestro.yml index 2aeafa9fba..8835e94594 100644 --- a/.github/workflows/maestro.yml +++ b/.github/workflows/maestro.yml @@ -79,7 +79,7 @@ jobs: uses: actions/download-artifact@v4 with: name: elementx-apk-maestro - - uses: mobile-dev-inc/action-maestro-cloud@v1.9.2 + - uses: mobile-dev-inc/action-maestro-cloud@v1.9.4 if: (github.event_name == 'pull_request' && github.event.pull_request.fork == null) || github.event_name == 'workflow_dispatch' with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} From b085c1b3d4560ac53bd779658d757b23e223362d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 Nov 2024 12:16:05 +0100 Subject: [PATCH 40/50] Session verification: add new screen to get ready on the other session. --- .../outgoing/VerifySelfSessionPresenter.kt | 4 ++++ .../impl/outgoing/VerifySelfSessionState.kt | 1 + .../outgoing/VerifySelfSessionStateMachine.kt | 15 +++++++++++--- .../VerifySelfSessionStateProvider.kt | 3 +++ .../impl/outgoing/VerifySelfSessionView.kt | 20 +++++++++++++++++-- .../outgoing/VerifySelfSessionViewEvents.kt | 1 + .../impl/src/main/res/values/localazy.xml | 2 ++ 7 files changed, 41 insertions(+), 5 deletions(-) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenter.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenter.kt index 360bf64875..97778b322b 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenter.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenter.kt @@ -104,6 +104,7 @@ class VerifySelfSessionPresenter @AssistedInject constructor( fun handleEvents(event: VerifySelfSessionViewEvents) { Timber.d("Verification user action: ${event::class.simpleName}") when (event) { + VerifySelfSessionViewEvents.UseAnotherDevice -> stateAndDispatch.dispatchAction(StateMachineEvent.UseAnotherDevice) VerifySelfSessionViewEvents.RequestVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.RequestVerification) VerifySelfSessionViewEvents.StartSasVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.StartSasVerification) VerifySelfSessionViewEvents.ConfirmVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.AcceptChallenge) @@ -134,6 +135,9 @@ class VerifySelfSessionPresenter @AssistedInject constructor( isLastDevice = encryptionService.isLastDevice.value ) } + VerifySelfSessionStateMachine.State.UseAnotherDevice -> { + VerifySelfSessionState.Step.UseAnotherDevice + } StateMachineState.RequestingVerification, StateMachineState.StartingSasVerification, StateMachineState.SasVerificationStarted, diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionState.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionState.kt index e763305caa..32664b347f 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionState.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionState.kt @@ -26,6 +26,7 @@ data class VerifySelfSessionState( // FIXME canEnterRecoveryKey value is never read. data class Initial(val canEnterRecoveryKey: Boolean, val isLastDevice: Boolean = false) : Step + data object UseAnotherDevice : Step data object Canceled : Step data object AwaitingOtherDeviceResponse : Step data object Ready : Step diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt index f423b14aae..09f18538d0 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt @@ -38,12 +38,14 @@ class VerifySelfSessionStateMachine @Inject constructor( init { spec { inState { + on { _: Event.UseAnotherDevice, state -> + state.override { State.UseAnotherDevice.andLogStateChange() } + } + } + inState { on { _: Event.RequestVerification, state -> state.override { State.RequestingVerification.andLogStateChange() } } - on { _: Event.StartSasVerification, state -> - state.override { State.StartingSasVerification.andLogStateChange() } - } } inState { onEnterEffect { @@ -119,6 +121,7 @@ class VerifySelfSessionStateMachine @Inject constructor( on { _: Event.Cancel, state: MachineState -> when (state.snapshot) { State.Initial, State.Completed, State.Canceled -> state.noChange() + State.UseAnotherDevice -> state.override { State.Initial.andLogStateChange() } // For some reason `cancelVerification` is not calling its delegate `didCancel` method so we don't pass from // `Canceling` state to `Canceled` automatically anymore else -> { @@ -144,6 +147,9 @@ class VerifySelfSessionStateMachine @Inject constructor( /** The initial state, before verification started. */ data object Initial : State + /** Let the user know that they need to get ready on their other session. */ + data object UseAnotherDevice : State + /** Waiting for verification acceptance. */ data object RequestingVerification : State @@ -175,6 +181,9 @@ class VerifySelfSessionStateMachine @Inject constructor( } sealed interface Event { + /** User wants to use another session. */ + data object UseAnotherDevice : Event + /** Request verification. */ data object RequestVerification : Event diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateProvider.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateProvider.kt index 8cb60a822f..b9de96aae4 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateProvider.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateProvider.kt @@ -56,6 +56,9 @@ open class VerifySelfSessionStateProvider : PreviewParameterProvider state.eventSink(VerifySelfSessionViewEvents.Reset) - is Step.AwaitingOtherDeviceResponse, Step.Ready -> state.eventSink(VerifySelfSessionViewEvents.Cancel) + is Step.AwaitingOtherDeviceResponse, + Step.UseAnotherDevice, + Step.Ready -> state.eventSink(VerifySelfSessionViewEvents.Cancel) is Step.Verifying -> { if (!step.state.isLoading()) { state.eventSink(VerifySelfSessionViewEvents.DeclineVerification) @@ -159,6 +161,7 @@ fun VerifySelfSessionView( private fun VerifySelfSessionHeader(step: Step) { val iconStyle = when (step) { Step.Loading -> error("Should not happen") + Step.UseAnotherDevice -> BigIcon.Style.Default(CompoundIcons.Devices()) is Step.Initial, Step.AwaitingOtherDeviceResponse -> BigIcon.Style.Default(CompoundIcons.LockSolid()) Step.Canceled -> BigIcon.Style.AlertSolid Step.Ready, is Step.Verifying -> BigIcon.Style.Default(CompoundIcons.Reaction()) @@ -167,6 +170,7 @@ private fun VerifySelfSessionHeader(step: Step) { } val titleTextId = when (step) { Step.Loading -> error("Should not happen") + Step.UseAnotherDevice -> R.string.screen_session_verification_use_another_device_title is Step.Initial, Step.AwaitingOtherDeviceResponse -> R.string.screen_identity_confirmation_title Step.Canceled -> CommonStrings.common_verification_cancelled Step.Ready -> R.string.screen_session_verification_compare_emojis_title @@ -179,6 +183,7 @@ private fun VerifySelfSessionHeader(step: Step) { } val subtitleTextId = when (step) { Step.Loading -> error("Should not happen") + Step.UseAnotherDevice -> R.string.screen_session_verification_use_another_device_subtitle is Step.Initial, Step.AwaitingOtherDeviceResponse -> R.string.screen_identity_confirmation_subtitle Step.Canceled -> R.string.screen_session_verification_cancelled_subtitle Step.Ready -> R.string.screen_session_verification_ready_subtitle @@ -252,7 +257,7 @@ private fun VerifySelfSessionBottomMenu( Button( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.screen_identity_use_another_device), - onClick = { eventSink(VerifySelfSessionViewEvents.RequestVerification) }, + onClick = { eventSink(VerifySelfSessionViewEvents.UseAnotherDevice) }, ) } Button( @@ -267,6 +272,17 @@ private fun VerifySelfSessionBottomMenu( ) } } + is Step.UseAnotherDevice -> { + VerificationBottomMenu { + Button( + modifier = Modifier.fillMaxWidth(), + text = stringResource(CommonStrings.action_start_verification), + onClick = { eventSink(VerifySelfSessionViewEvents.RequestVerification) }, + ) + // Placeholder so the 1st button keeps its vertical position + Spacer(modifier = Modifier.height(40.dp)) + } + } is Step.Canceled -> { VerificationBottomMenu { Button( diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewEvents.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewEvents.kt index 869bdc7051..b4af38f780 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewEvents.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewEvents.kt @@ -8,6 +8,7 @@ package io.element.android.features.verifysession.impl.outgoing sealed interface VerifySelfSessionViewEvents { + data object UseAnotherDevice : VerifySelfSessionViewEvents data object RequestVerification : VerifySelfSessionViewEvents data object StartSasVerification : VerifySelfSessionViewEvents data object ConfirmVerification : VerifySelfSessionViewEvents diff --git a/features/verifysession/impl/src/main/res/values/localazy.xml b/features/verifysession/impl/src/main/res/values/localazy.xml index f67a2024b9..85fa3b8246 100644 --- a/features/verifysession/impl/src/main/res/values/localazy.xml +++ b/features/verifysession/impl/src/main/res/values/localazy.xml @@ -35,6 +35,8 @@ "Verification requested" "They don’t match" "They match" + "Make sure you have the app open in the other device before starting verification from here." + "Open the app on another verified device" "Accept the request to start the verification process in your other session to continue." "Waiting to accept request" "Signing out…" From d0a64a16df1f11005116763f08057218445e04fc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 Nov 2024 12:21:38 +0100 Subject: [PATCH 41/50] Session verification: Iterate on the waiting for other device screen. --- .../impl/outgoing/VerifySelfSessionView.kt | 10 +++++++--- .../impl/src/main/res/values/localazy.xml | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt index cbc79a219f..3013dc7613 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt @@ -161,8 +161,9 @@ fun VerifySelfSessionView( private fun VerifySelfSessionHeader(step: Step) { val iconStyle = when (step) { Step.Loading -> error("Should not happen") + is Step.Initial -> BigIcon.Style.Default(CompoundIcons.LockSolid()) Step.UseAnotherDevice -> BigIcon.Style.Default(CompoundIcons.Devices()) - is Step.Initial, Step.AwaitingOtherDeviceResponse -> BigIcon.Style.Default(CompoundIcons.LockSolid()) + Step.AwaitingOtherDeviceResponse -> BigIcon.Style.Default(CompoundIcons.Devices()) Step.Canceled -> BigIcon.Style.AlertSolid Step.Ready, is Step.Verifying -> BigIcon.Style.Default(CompoundIcons.Reaction()) Step.Completed -> BigIcon.Style.SuccessSolid @@ -170,8 +171,9 @@ private fun VerifySelfSessionHeader(step: Step) { } val titleTextId = when (step) { Step.Loading -> error("Should not happen") + is Step.Initial -> R.string.screen_identity_confirmation_title Step.UseAnotherDevice -> R.string.screen_session_verification_use_another_device_title - is Step.Initial, Step.AwaitingOtherDeviceResponse -> R.string.screen_identity_confirmation_title + Step.AwaitingOtherDeviceResponse -> R.string.screen_session_verification_waiting_another_device_title Step.Canceled -> CommonStrings.common_verification_cancelled Step.Ready -> R.string.screen_session_verification_compare_emojis_title Step.Completed -> R.string.screen_identity_confirmed_title @@ -183,8 +185,9 @@ private fun VerifySelfSessionHeader(step: Step) { } val subtitleTextId = when (step) { Step.Loading -> error("Should not happen") + is Step.Initial -> R.string.screen_identity_confirmation_subtitle Step.UseAnotherDevice -> R.string.screen_session_verification_use_another_device_subtitle - is Step.Initial, Step.AwaitingOtherDeviceResponse -> R.string.screen_identity_confirmation_subtitle + Step.AwaitingOtherDeviceResponse -> R.string.screen_session_verification_waiting_another_device_subtitle Step.Canceled -> R.string.screen_session_verification_cancelled_subtitle Step.Ready -> R.string.screen_session_verification_ready_subtitle Step.Completed -> R.string.screen_identity_confirmed_subtitle @@ -318,6 +321,7 @@ private fun VerifySelfSessionBottomMenu( text = stringResource(R.string.screen_identity_waiting_on_other_device), onClick = {}, showProgress = true, + enabled = false, ) // Placeholder so the 1st button keeps its vertical position Spacer(modifier = Modifier.height(40.dp)) diff --git a/features/verifysession/impl/src/main/res/values/localazy.xml b/features/verifysession/impl/src/main/res/values/localazy.xml index 85fa3b8246..db18590cbe 100644 --- a/features/verifysession/impl/src/main/res/values/localazy.xml +++ b/features/verifysession/impl/src/main/res/values/localazy.xml @@ -37,6 +37,8 @@ "They match" "Make sure you have the app open in the other device before starting verification from here." "Open the app on another verified device" + "You should see a popup on the other device. Start the verification from there now." + "Start verification on the other device" "Accept the request to start the verification process in your other session to continue." "Waiting to accept request" "Signing out…" From 2b061085741a63ecc3ef2e06e3bf320b60a5ce5f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 Nov 2024 12:43:02 +0100 Subject: [PATCH 42/50] Update wording for verification cancelled. --- .../verifysession/impl/outgoing/VerifySelfSessionView.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt index 3013dc7613..4351bb0bf2 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt @@ -174,7 +174,7 @@ private fun VerifySelfSessionHeader(step: Step) { is Step.Initial -> R.string.screen_identity_confirmation_title Step.UseAnotherDevice -> R.string.screen_session_verification_use_another_device_title Step.AwaitingOtherDeviceResponse -> R.string.screen_session_verification_waiting_another_device_title - Step.Canceled -> CommonStrings.common_verification_cancelled + Step.Canceled -> CommonStrings.common_verification_failed Step.Ready -> R.string.screen_session_verification_compare_emojis_title Step.Completed -> R.string.screen_identity_confirmed_title is Step.Verifying -> when (step.data) { @@ -188,7 +188,7 @@ private fun VerifySelfSessionHeader(step: Step) { is Step.Initial -> R.string.screen_identity_confirmation_subtitle Step.UseAnotherDevice -> R.string.screen_session_verification_use_another_device_subtitle Step.AwaitingOtherDeviceResponse -> R.string.screen_session_verification_waiting_another_device_subtitle - Step.Canceled -> R.string.screen_session_verification_cancelled_subtitle + Step.Canceled -> R.string.screen_session_verification_failed_subtitle Step.Ready -> R.string.screen_session_verification_ready_subtitle Step.Completed -> R.string.screen_identity_confirmed_subtitle is Step.Verifying -> when (step.data) { From 683b694eec9e3c26526aaac42ec7fcc2d199a37b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 Nov 2024 12:46:13 +0100 Subject: [PATCH 43/50] Incoming verification: do not distinguish UI between cancelled and failed state. --- .../verifysession/impl/incoming/IncomingVerificationView.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt index cc2c9fbd2d..e8f098f848 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt @@ -80,14 +80,14 @@ fun IncomingVerificationView( @Composable private fun IncomingVerificationHeader(step: Step) { val iconStyle = when (step) { - Step.Canceled, + Step.Canceled -> BigIcon.Style.AlertSolid is Step.Initial -> BigIcon.Style.Default(CompoundIcons.LockSolid()) is Step.Verifying -> BigIcon.Style.Default(CompoundIcons.Reaction()) Step.Completed -> BigIcon.Style.SuccessSolid Step.Failure -> BigIcon.Style.AlertSolid } val titleTextId = when (step) { - Step.Canceled -> CommonStrings.common_verification_cancelled + Step.Canceled -> R.string.screen_session_verification_request_failure_title is Step.Initial -> R.string.screen_session_verification_request_title is Step.Verifying -> when (step.data) { is SessionVerificationData.Decimals -> R.string.screen_session_verification_compare_numbers_title @@ -97,7 +97,7 @@ private fun IncomingVerificationHeader(step: Step) { Step.Failure -> R.string.screen_session_verification_request_failure_title } val subtitleTextId = when (step) { - Step.Canceled -> R.string.screen_session_verification_cancelled_subtitle + Step.Canceled -> R.string.screen_session_verification_request_failure_subtitle is Step.Initial -> R.string.screen_session_verification_request_subtitle is Step.Verifying -> when (step.data) { is SessionVerificationData.Decimals -> R.string.screen_session_verification_compare_numbers_subtitle From 39df0c5187cb0a5b92e9a0daae34d190fcb6cf4b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 12:56:48 +0100 Subject: [PATCH 44/50] Update dependency com.otaliastudios:transcoder to v0.11.2 (#3805) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4737ef942a..a638a379a5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -182,7 +182,7 @@ sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions", sqlcipher = "net.zetetic:android-database-sqlcipher:4.5.4" sqlite = "androidx.sqlite:sqlite-ktx:2.4.0" unifiedpush = "com.github.UnifiedPush:android-connector:2.4.0" -otaliastudios_transcoder = "com.otaliastudios:transcoder:0.11.1" +otaliastudios_transcoder = "com.otaliastudios:transcoder:0.11.2" vanniktech_blurhash = "com.vanniktech:blurhash:0.3.0" telephoto_zoomableimage = { module = "me.saket.telephoto:zoomable-image-coil", version.ref = "telephoto" } telephoto_flick = { module = "me.saket.telephoto:flick-android", version.ref = "telephoto" } From 2386350abbd544436b83f4a8b60a5664474a9699 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:25:05 +0100 Subject: [PATCH 45/50] Update plugin paparazzi to v1.3.5 (#3826) * Update plugin paparazzi to v1.3.5 * Update screenshots --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: ElementBot --- gradle/libs.versions.toml | 2 +- ...re.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png | 4 ++-- ...re.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png | 4 ++-- ....resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png | 4 ++-- ....resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_2_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Night_2_en.png | 4 ++-- ....members.moderation_RoomMembersModerationView_Day_0_en.png | 4 ++-- ....members.moderation_RoomMembersModerationView_Day_1_en.png | 4 ++-- ....members.moderation_RoomMembersModerationView_Day_2_en.png | 4 ++-- ...embers.moderation_RoomMembersModerationView_Night_0_en.png | 4 ++-- ...embers.moderation_RoomMembersModerationView_Night_1_en.png | 4 ++-- ...embers.moderation_RoomMembersModerationView_Night_2_en.png | 4 ++-- ...l.rolesandpermissions_RolesAndPermissionsView_Day_2_en.png | 4 ++-- ...rolesandpermissions_RolesAndPermissionsView_Night_2_en.png | 4 ++-- ...components_DefaultRoomListTopBarWithIndicator_Day_0_en.png | 4 ++-- ...mponents_DefaultRoomListTopBarWithIndicator_Night_0_en.png | 4 ++-- ...oomlist.impl.components_DefaultRoomListTopBar_Day_0_en.png | 4 ++-- ...mlist.impl.components_DefaultRoomListTopBar_Night_0_en.png | 4 ++-- .../images/features.roomlist.impl_RoomListView_Day_0_en.png | 4 ++-- .../images/features.roomlist.impl_RoomListView_Day_10_en.png | 4 ++-- .../images/features.roomlist.impl_RoomListView_Day_1_en.png | 4 ++-- .../images/features.roomlist.impl_RoomListView_Day_2_en.png | 4 ++-- .../images/features.roomlist.impl_RoomListView_Day_3_en.png | 4 ++-- .../images/features.roomlist.impl_RoomListView_Day_4_en.png | 4 ++-- .../images/features.roomlist.impl_RoomListView_Day_5_en.png | 4 ++-- .../images/features.roomlist.impl_RoomListView_Day_6_en.png | 4 ++-- .../images/features.roomlist.impl_RoomListView_Night_0_en.png | 4 ++-- .../features.roomlist.impl_RoomListView_Night_10_en.png | 4 ++-- .../images/features.roomlist.impl_RoomListView_Night_1_en.png | 4 ++-- .../images/features.roomlist.impl_RoomListView_Night_2_en.png | 4 ++-- .../images/features.roomlist.impl_RoomListView_Night_3_en.png | 4 ++-- .../images/features.roomlist.impl_RoomListView_Night_4_en.png | 4 ++-- .../images/features.roomlist.impl_RoomListView_Night_5_en.png | 4 ++-- .../images/features.roomlist.impl_RoomListView_Night_6_en.png | 4 ++-- ...st_item_-_selection_in_trailing_content_List_items_en.png} | 0 ...ist_item_-_selection_in_supporting_text_List_items_en.png} | 0 ...iple_selection_List_item_-_no_selection_List_items_en.png} | 0 ..._selection_List_item_-_custom_formatter_List_items_en.png} | 0 ...ist_item_-_selection_in_supporting_text_List_items_en.png} | 0 ...st_item_-_selection_in_trailing_content_List_items_en.png} | 0 ...st_item_-_no_selection,_supporting_text_List_items_en.png} | 0 ...ngle_selection_List_item_-_no_selection_List_items_en.png} | 0 ...tItemEmpty_Text_field_List_item_-_empty_List_items_en.png} | 0 ...e_Text_field_List_item_-_textfieldvalue_List_items_en.png} | 0 ...eldListItem_Text_field_List_item_-_text_List_items_en.png} | 0 .../libraries.designsystem.components_Bloom_Day_0_en.png | 4 ++-- .../libraries.designsystem.components_Bloom_Night_0_en.png | 4 ++-- ...components.previews_DatePickerDark_DateTime pickers_en.png | 3 --- ...components.previews_DatePickerDark_DateTime_pickers_en.png | 3 +++ ...omponents.previews_DatePickerLight_DateTime pickers_en.png | 3 --- ...omponents.previews_DatePickerLight_DateTime_pickers_en.png | 3 +++ ...nts.previews_TimePickerHorizontal_DateTime_pickers_en.png} | 0 ...s.previews_TimePickerVerticalDark_DateTime_pickers_en.png} | 0 ....previews_TimePickerVerticalLight_DateTime_pickers_en.png} | 0 ...ents_CircularProgressIndicator_Progress_Indicators_en.png} | 0 ...ctiveButton_Dialog_with_destructive_button_Dialogs_en.png} | 0 ...ton_Dialog_with_only_message_and_ok_button_Dialogs_en.png} | 0 ...ogWithThirdButton_Dialog_with_third_button_Dialogs_en.png} | 0 ...ndOkButton_Dialog_with_title_and_ok_button_Dialogs_en.png} | 0 ...tton_Dialog_with_title,_icon_and_ok_button_Dialogs_en.png} | 0 ...nents_FloatingActionButton_Floating_Action_Buttons_en.png} | 0 ...onents_LinearProgressIndicator_Progress_Indicators_en.png} | 0 ...hIcon_List_item_-_Primary_action_&_Icon_List_items_en.png} | 0 ...thIcons_List_item_(1_line)_-_Both_Icons_List_items_en.png} | 0 ...x_List_item_(1_line)_-_Leading_Checkbox_List_items_en.png} | 0 ...gIcon_List_item_(1_line)_-_Leading_Icon_List_items_en.png} | 0 ...ist_item_(1_line)_-_Leading_RadioButton_List_items_en.png} | 0 ...tch_List_item_(1_line)_-_Leading_Switch_List_items_en.png} | 0 ...eLineSimple_List_item_(1_line)_-_Simple_List_items_en.png} | 0 ..._List_item_(1_line)_-_Trailing_Checkbox_List_items_en.png} | 0 ...Icon_List_item_(1_line)_-_Trailing_Icon_List_items_en.png} | 0 ...st_item_(1_line)_-_Trailing_RadioButton_List_items_en.png} | 0 ...ch_List_item_(1_line)_-_Trailing_Switch_List_items_en.png} | 0 ...hIcons_List_item_(3_lines)_-_Both_Icons_List_items_en.png} | 0 ..._List_item_(3_lines)_-_Leading_Checkbox_List_items_en.png} | 0 ...Icon_List_item_(3_lines)_-_Leading_Icon_List_items_en.png} | 0 ...st_item_(3_lines)_-_Leading_RadioButton_List_items_en.png} | 0 ...ch_List_item_(3_lines)_-_Leading_Switch_List_items_en.png} | 0 ...inesSimple_List_item_(3_lines)_-_Simple_List_items_en.png} | 0 ...List_item_(3_lines)_-_Trailing_Checkbox_List_items_en.png} | 0 ...con_List_item_(3_lines)_-_Trailing_Icon_List_items_en.png} | 0 ...t_item_(3_lines)_-_Trailing_RadioButton_List_items_en.png} | 0 ...h_List_item_(3_lines)_-_Trailing_Switch_List_items_en.png} | 0 ...ist_item_(2_lines)_-_Both_Icons_-_Error_List_items_en.png} | 0 ...hIcons_List_item_(2_lines)_-_Both_Icons_List_items_en.png} | 0 ...em_(2_lines)_-_Leading_Checkbox_-_Error_List_items_en.png} | 0 ..._List_item_(2_lines)_-_Leading_Checkbox_List_items_en.png} | 0 ...t_item_(2_lines)_-_Leading_Icon_-_Error_List_items_en.png} | 0 ...Icon_List_item_(2_lines)_-_Leading_Icon_List_items_en.png} | 0 ...(2_lines)_-_Leading_RadioButton_-_Error_List_items_en.png} | 0 ...st_item_(2_lines)_-_Leading_RadioButton_List_items_en.png} | 0 ...item_(2_lines)_-_Leading_Switch_-_Error_List_items_en.png} | 0 ...ch_List_item_(2_lines)_-_Leading_Switch_List_items_en.png} | 0 ...or_List_item_(2_lines)_-_Simple_-_Error_List_items_en.png} | 0 ...inesSimple_List_item_(2_lines)_-_Simple_List_items_en.png} | 0 ...m_(2_lines)_-_Trailing_Checkbox_-_Error_List_items_en.png} | 0 ...List_item_(2_lines)_-_Trailing_Checkbox_List_items_en.png} | 0 ..._item_(2_lines)_-_Trailing_Icon_-_Error_List_items_en.png} | 0 ...con_List_item_(2_lines)_-_Trailing_Icon_List_items_en.png} | 0 ...2_lines)_-_Trailing_RadioButton_-_Error_List_items_en.png} | 0 ...t_item_(2_lines)_-_Trailing_RadioButton_List_items_en.png} | 0 ...tem_(2_lines)_-_Trailing_Switch_-_Error_List_items_en.png} | 0 ...h_List_item_(2_lines)_-_Trailing_Switch_List_items_en.png} | 0 ..._header_with_description_and_divider_List_sections_en.png} | 0 ...List_section_header_with_description_List_sections_en.png} | 0 ...der_List_section_header_with_divider_List_sections_en.png} | 0 ...istSectionHeader_List_section_header_List_sections_en.png} | 0 ...ist_supporting_text_-_custom_padding_List_sections_en.png} | 0 ...st_supporting_text_-_default_padding_List_sections_en.png} | 0 ...List_supporting_text_-_large_padding_List_sections_en.png} | 0 ...ng_List_supporting_text_-_no_padding_List_sections_en.png} | 0 ...List_supporting_text_-_small_padding_List_sections_en.png} | 0 ...gnsystem.theme.components_MediumTopAppBar_App_Bars_en.png} | 0 ...theme.components_ModalBottomSheetDark_Bottom Sheets_en.png | 3 --- ...theme.components_ModalBottomSheetDark_Bottom_Sheets_en.png | 3 +++ ...heme.components_ModalBottomSheetLight_Bottom Sheets_en.png | 3 --- ...heme.components_ModalBottomSheetLight_Bottom_Sheets_en.png | 3 +++ ...e.components_SearchBarActiveNoneQuery_Search_views_en.png} | 0 ...components_SearchBarActiveWithContent_Search_views_en.png} | 0 ...mponents_SearchBarActiveWithNoResults_Search_views_en.png} | 0 ..._SearchBarActiveWithQueryNoBackButton_Search_views_en.png} | 0 ...e.components_SearchBarActiveWithQuery_Search_views_en.png} | 0 ...em.theme.components_SearchBarInactive_Search_views_en.png} | 0 ...on_Snackbar_with_action_and_close_button_Snackbars_en.png} | 0 ...with_action_and_close_button_on_new_line_Snackbars_en.png} | 0 ...NewLine_Snackbar_with_action_on_new_line_Snackbars_en.png} | 0 ..._SnackbarWithAction_Snackbar_with_action_Snackbars_en.png} | 0 ...s.designsystem.theme.components_TopAppBar_App_Bars_en.png} | 0 ....matrix.ui.components_AvatarActionBottomSheet_Day_0_en.png | 4 ++-- ...atrix.ui.components_AvatarActionBottomSheet_Night_0_en.png | 4 ++-- 131 files changed, 89 insertions(+), 89 deletions(-) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.components.list_MutipleSelectionListItemSelectedTrailingContent_Multiple selection List item - selection in trailing content_List items_en.png => libraries.designsystem.components.list_MutipleSelectionListItemSelectedTrailingContent_Multiple_selection_List_item_-_selection_in_trailing_content_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple selection List item - selection in supporting text_List items_en.png => libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple_selection_List_item_-_selection_in_supporting_text_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.components.list_MutipleSelectionListItem_Multiple selection List item - no selection_List items_en.png => libraries.designsystem.components.list_MutipleSelectionListItem_Multiple_selection_List_item_-_no_selection_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.components.list_SingleSelectionListItemCustomFormattert_Single selection List item - custom formatter_List items_en.png => libraries.designsystem.components.list_SingleSelectionListItemCustomFormattert_Single_selection_List_item_-_custom_formatter_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.components.list_SingleSelectionListItemSelectedInSupportingText_Single selection List item - selection in supporting text_List items_en.png => libraries.designsystem.components.list_SingleSelectionListItemSelectedInSupportingText_Single_selection_List_item_-_selection_in_supporting_text_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.components.list_SingleSelectionListItemSelectedInTrailingContent_Single selection List item - selection in trailing content_List items_en.png => libraries.designsystem.components.list_SingleSelectionListItemSelectedInTrailingContent_Single_selection_List_item_-_selection_in_trailing_content_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.components.list_SingleSelectionListItemUnselectedWithSupportingText_Single selection List item - no selection, supporting text_List items_en.png => libraries.designsystem.components.list_SingleSelectionListItemUnselectedWithSupportingText_Single_selection_List_item_-_no_selection,_supporting_text_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.components.list_SingleSelectionListItem_Single selection List item - no selection_List items_en.png => libraries.designsystem.components.list_SingleSelectionListItem_Single_selection_List_item_-_no_selection_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.components.list_TextFieldListItemEmpty_Text field List item - empty_List items_en.png => libraries.designsystem.components.list_TextFieldListItemEmpty_Text_field_List_item_-_empty_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text field List item - textfieldvalue_List items_en.png => libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text_field_List_item_-_textfieldvalue_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.components.list_TextFieldListItem_Text field List item - text_List items_en.png => libraries.designsystem.components.list_TextFieldListItem_Text_field_List_item_-_text_List_items_en.png} (100%) delete mode 100644 tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerDark_DateTime pickers_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerLight_DateTime pickers_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en.png rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime pickers_en.png => libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime pickers_en.png => libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime pickers_en.png => libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_CircularProgressIndicator_Progress Indicators_en.png => libraries.designsystem.theme.components_CircularProgressIndicator_Progress_Indicators_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_DialogWithDestructiveButton_Dialog with destructive button_Dialogs_en.png => libraries.designsystem.theme.components_DialogWithDestructiveButton_Dialog_with_destructive_button_Dialogs_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_DialogWithOnlyMessageAndOkButton_Dialog with only message and ok button_Dialogs_en.png => libraries.designsystem.theme.components_DialogWithOnlyMessageAndOkButton_Dialog_with_only_message_and_ok_button_Dialogs_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_DialogWithThirdButton_Dialog with third button_Dialogs_en.png => libraries.designsystem.theme.components_DialogWithThirdButton_Dialog_with_third_button_Dialogs_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_DialogWithTitleAndOkButton_Dialog with title and ok button_Dialogs_en.png => libraries.designsystem.theme.components_DialogWithTitleAndOkButton_Dialog_with_title_and_ok_button_Dialogs_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_DialogWithTitleIconAndOkButton_Dialog with title, icon and ok button_Dialogs_en.png => libraries.designsystem.theme.components_DialogWithTitleIconAndOkButton_Dialog_with_title,_icon_and_ok_button_Dialogs_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_FloatingActionButton_Floating Action Buttons_en.png => libraries.designsystem.theme.components_FloatingActionButton_Floating_Action_Buttons_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_LinearProgressIndicator_Progress Indicators_en.png => libraries.designsystem.theme.components_LinearProgressIndicator_Progress_Indicators_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List item - Primary action & Icon_List items_en.png => libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List_item_-_Primary_action_&_Icon_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemSingleLineBothIcons_List item (1 line) - Both Icons_List items_en.png => libraries.designsystem.theme.components_ListItemSingleLineBothIcons_List_item_(1_line)_-_Both_Icons_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemSingleLineLeadingCheckbox_List item (1 line) - Leading Checkbox_List items_en.png => libraries.designsystem.theme.components_ListItemSingleLineLeadingCheckbox_List_item_(1_line)_-_Leading_Checkbox_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemSingleLineLeadingIcon_List item (1 line) - Leading Icon_List items_en.png => libraries.designsystem.theme.components_ListItemSingleLineLeadingIcon_List_item_(1_line)_-_Leading_Icon_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemSingleLineLeadingRadioButton_List item (1 line) - Leading RadioButton_List items_en.png => libraries.designsystem.theme.components_ListItemSingleLineLeadingRadioButton_List_item_(1_line)_-_Leading_RadioButton_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemSingleLineLeadingSwitch_List item (1 line) - Leading Switch_List items_en.png => libraries.designsystem.theme.components_ListItemSingleLineLeadingSwitch_List_item_(1_line)_-_Leading_Switch_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemSingleLineSimple_List item (1 line) - Simple_List items_en.png => libraries.designsystem.theme.components_ListItemSingleLineSimple_List_item_(1_line)_-_Simple_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemSingleLineTrailingCheckBox_List item (1 line) - Trailing Checkbox_List items_en.png => libraries.designsystem.theme.components_ListItemSingleLineTrailingCheckBox_List_item_(1_line)_-_Trailing_Checkbox_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemSingleLineTrailingIcon_List item (1 line) - Trailing Icon_List items_en.png => libraries.designsystem.theme.components_ListItemSingleLineTrailingIcon_List_item_(1_line)_-_Trailing_Icon_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemSingleLineTrailingRadioButton_List item (1 line) - Trailing RadioButton_List items_en.png => libraries.designsystem.theme.components_ListItemSingleLineTrailingRadioButton_List_item_(1_line)_-_Trailing_RadioButton_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemSingleLineTrailingSwitch_List item (1 line) - Trailing Switch_List items_en.png => libraries.designsystem.theme.components_ListItemSingleLineTrailingSwitch_List_item_(1_line)_-_Trailing_Switch_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemThreeLinesBothIcons_List item (3 lines) - Both Icons_List items_en.png => libraries.designsystem.theme.components_ListItemThreeLinesBothIcons_List_item_(3_lines)_-_Both_Icons_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemThreeLinesLeadingCheckbox_List item (3 lines) - Leading Checkbox_List items_en.png => libraries.designsystem.theme.components_ListItemThreeLinesLeadingCheckbox_List_item_(3_lines)_-_Leading_Checkbox_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemThreeLinesLeadingIcon_List item (3 lines) - Leading Icon_List items_en.png => libraries.designsystem.theme.components_ListItemThreeLinesLeadingIcon_List_item_(3_lines)_-_Leading_Icon_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemThreeLinesLeadingRadioButton_List item (3 lines) - Leading RadioButton_List items_en.png => libraries.designsystem.theme.components_ListItemThreeLinesLeadingRadioButton_List_item_(3_lines)_-_Leading_RadioButton_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemThreeLinesLeadingSwitch_List item (3 lines) - Leading Switch_List items_en.png => libraries.designsystem.theme.components_ListItemThreeLinesLeadingSwitch_List_item_(3_lines)_-_Leading_Switch_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemThreeLinesSimple_List item (3 lines) - Simple_List items_en.png => libraries.designsystem.theme.components_ListItemThreeLinesSimple_List_item_(3_lines)_-_Simple_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemThreeLinesTrailingCheckBox_List item (3 lines) - Trailing Checkbox_List items_en.png => libraries.designsystem.theme.components_ListItemThreeLinesTrailingCheckBox_List_item_(3_lines)_-_Trailing_Checkbox_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemThreeLinesTrailingIcon_List item (3 lines) - Trailing Icon_List items_en.png => libraries.designsystem.theme.components_ListItemThreeLinesTrailingIcon_List_item_(3_lines)_-_Trailing_Icon_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemThreeLinesTrailingRadioButton_List item (3 lines) - Trailing RadioButton_List items_en.png => libraries.designsystem.theme.components_ListItemThreeLinesTrailingRadioButton_List_item_(3_lines)_-_Trailing_RadioButton_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemThreeLinesTrailingSwitch_List item (3 lines) - Trailing Switch_List items_en.png => libraries.designsystem.theme.components_ListItemThreeLinesTrailingSwitch_List_item_(3_lines)_-_Trailing_Switch_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesBothIconsError_List item (2 lines) - Both Icons - Error_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesBothIconsError_List_item_(2_lines)_-_Both_Icons_-_Error_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesBothIcons_List item (2 lines) - Both Icons_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesBothIcons_List_item_(2_lines)_-_Both_Icons_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckboxError_List item (2 lines) - Leading Checkbox - Error_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckboxError_List_item_(2_lines)_-_Leading_Checkbox_-_Error_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckbox_List item (2 lines) - Leading Checkbox_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckbox_List_item_(2_lines)_-_Leading_Checkbox_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesLeadingIconError_List item (2 lines) - Leading Icon - Error_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesLeadingIconError_List_item_(2_lines)_-_Leading_Icon_-_Error_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesLeadingIcon_List item (2 lines) - Leading Icon_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesLeadingIcon_List_item_(2_lines)_-_Leading_Icon_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButtonError_List item (2 lines) - Leading RadioButton - Error_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButtonError_List_item_(2_lines)_-_Leading_RadioButton_-_Error_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButton_List item (2 lines) - Leading RadioButton_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButton_List_item_(2_lines)_-_Leading_RadioButton_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitchError_List item (2 lines) - Leading Switch - Error_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitchError_List_item_(2_lines)_-_Leading_Switch_-_Error_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitch_List item (2 lines) - Leading Switch_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitch_List_item_(2_lines)_-_Leading_Switch_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesSimpleError_List item (2 lines) - Simple - Error_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesSimpleError_List_item_(2_lines)_-_Simple_-_Error_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesSimple_List item (2 lines) - Simple_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesSimple_List_item_(2_lines)_-_Simple_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBoxError_List item (2 lines) - Trailing Checkbox - Error_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBoxError_List_item_(2_lines)_-_Trailing_Checkbox_-_Error_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBox_List item (2 lines) - Trailing Checkbox_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBox_List_item_(2_lines)_-_Trailing_Checkbox_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesTrailingIconError_List item (2 lines) - Trailing Icon - Error_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesTrailingIconError_List_item_(2_lines)_-_Trailing_Icon_-_Error_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesTrailingIcon_List item (2 lines) - Trailing Icon_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesTrailingIcon_List_item_(2_lines)_-_Trailing_Icon_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButtonError_List item (2 lines) - Trailing RadioButton - Error_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButtonError_List_item_(2_lines)_-_Trailing_RadioButton_-_Error_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButton_List item (2 lines) - Trailing RadioButton_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButton_List_item_(2_lines)_-_Trailing_RadioButton_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitchError_List item (2 lines) - Trailing Switch - Error_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitchError_List_item_(2_lines)_-_Trailing_Switch_-_Error_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitch_List item (2 lines) - Trailing Switch_List items_en.png => libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitch_List_item_(2_lines)_-_Trailing_Switch_List_items_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListSectionHeaderWithDescriptionAndDivider_List section header with description and divider_List sections_en.png => libraries.designsystem.theme.components_ListSectionHeaderWithDescriptionAndDivider_List_section_header_with_description_and_divider_List_sections_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListSectionHeaderWithDescription_List section header with description_List sections_en.png => libraries.designsystem.theme.components_ListSectionHeaderWithDescription_List_section_header_with_description_List_sections_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListSectionHeaderWithDivider_List section header with divider_List sections_en.png => libraries.designsystem.theme.components_ListSectionHeaderWithDivider_List_section_header_with_divider_List_sections_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListSectionHeader_List section header_List sections_en.png => libraries.designsystem.theme.components_ListSectionHeader_List_section_header_List_sections_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListSupportingTextCustomPadding_List supporting text - custom padding_List sections_en.png => libraries.designsystem.theme.components_ListSupportingTextCustomPadding_List_supporting_text_-_custom_padding_List_sections_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListSupportingTextDefaultPadding_List supporting text - default padding_List sections_en.png => libraries.designsystem.theme.components_ListSupportingTextDefaultPadding_List_supporting_text_-_default_padding_List_sections_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListSupportingTextLargePadding_List supporting text - large padding_List sections_en.png => libraries.designsystem.theme.components_ListSupportingTextLargePadding_List_supporting_text_-_large_padding_List_sections_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListSupportingTextNoPadding_List supporting text - no padding_List sections_en.png => libraries.designsystem.theme.components_ListSupportingTextNoPadding_List_supporting_text_-_no_padding_List_sections_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List supporting text - small padding_List sections_en.png => libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List_supporting_text_-_small_padding_List_sections_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_MediumTopAppBar_App Bars_en.png => libraries.designsystem.theme.components_MediumTopAppBar_App_Bars_en.png} (100%) delete mode 100644 tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom Sheets_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom Sheets_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en.png rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_SearchBarActiveNoneQuery_Search views_en.png => libraries.designsystem.theme.components_SearchBarActiveNoneQuery_Search_views_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_SearchBarActiveWithContent_Search views_en.png => libraries.designsystem.theme.components_SearchBarActiveWithContent_Search_views_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search views_en.png => libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search views_en.png => libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search_views_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search views_en.png => libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search_views_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_SearchBarInactive_Search views_en.png => libraries.designsystem.theme.components_SearchBarInactive_Search_views_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar with action and close button_Snackbars_en.png => libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar_with_action_and_close_button_Snackbars_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar with action and close button on new line_Snackbars_en.png => libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar_with_action_and_close_button_on_new_line_Snackbars_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar with action on new line_Snackbars_en.png => libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar_with_action_on_new_line_Snackbars_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_SnackbarWithAction_Snackbar with action_Snackbars_en.png => libraries.designsystem.theme.components_SnackbarWithAction_Snackbar_with_action_Snackbars_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.theme.components_TopAppBar_App Bars_en.png => libraries.designsystem.theme.components_TopAppBar_App_Bars_en.png} (100%) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a638a379a5..224dc9cec9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -237,7 +237,7 @@ ktlint = "org.jlleitschuh.gradle.ktlint:12.1.1" dependencygraph = "com.savvasdalkitsis.module-dependency-graph:0.12" dependencycheck = "org.owasp.dependencycheck:10.0.4" dependencyanalysis = { id = "com.autonomousapps.dependency-analysis", version.ref = "dependencyAnalysis" } -paparazzi = "app.cash.paparazzi:1.3.4" +paparazzi = "app.cash.paparazzi:1.3.5" sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } firebaseAppDistribution = { id = "com.google.firebase.appdistribution", version.ref = "firebaseAppDistribution" } knit = { id = "org.jetbrains.kotlinx.knit", version = "0.5.0" } diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png index 89bbbe7c2a..8ad64f6d22 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a48e666ed705b5c873e423e2555c96a7549bbc25dfd5499ba77feb2cc403784 -size 56615 +oid sha256:b1bf1424ee914c60298255f489b5df011c930112159f8b978152727ab1f7a553 +size 56543 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png index 0f8da5dc68..cbd07418a2 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7338522b75d002cd88b31cbdf245a2143b28862ca2b5fa0f82d9df1c66de5d04 -size 54719 +oid sha256:650304d70f57fab91df310d61ae5a816d46c7d00c2b0f02887f176ecdeccc8c8 +size 54643 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png index ddb66a771b..3720d75672 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:708d72610cdddcd525ae27dd695d6cecfd2227e6b6b9dd4d5398befc1a414693 -size 55118 +oid sha256:4a1bdd84ad99effe4448d089473851431c886c1e79f025eb7bf2b52e5b5bfca0 +size 54851 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png index 94ca63b15e..b0a41c0001 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4936ee16763c77f1c76a85ad2cf137cf92a3547c0e4d2b64158244e9d0edbe58 -size 52900 +oid sha256:4bf367d9a4ab122b34fa4daa0bdcdd2488360f571d51ed503a82472073a31828 +size 52717 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_2_en.png index 61ec6b71aa..9444673709 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5c516399c67cbaf59e9565b3083481eeae2d2fcd684608bc29d8bd456eda9f6 -size 25800 +oid sha256:dca6dfd32a2128fe68de26e03156742cbbfd3cb858f82871c940a693516ce223 +size 39171 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_2_en.png index 9721de1253..515b536adb 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2977fda41dcaf1c6528dee113781f81329fc31a76338ee81768e3b858394178 -size 23947 +oid sha256:b4d9f0142ed6d83339bfadd1736a1f832bffed776f31f7995f0afdace690a2c3 +size 37300 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en.png index 5aec452dc0..e7cbc171c5 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:346b55226273559de0d0d7298ef877c7f4e5441cd672bf224c2eead14d336c76 -size 16835 +oid sha256:055c81bf67eade1f203cf3e02a2b5096c89f73918b3f9123f0926d9af7a4175c +size 16744 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en.png index 31d097182e..d9479c2777 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5cf37333070202ab26168d65639fd3913e1dd2cd0508b52e021789d4827ece3 -size 21362 +oid sha256:a4cf5881fe38c4d666a7c61d64d58ecc22fb82e50931857a79370c14301af444 +size 21293 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en.png index 3b75bbad3d..0ad86e2683 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49d02751c7a36b988249c03e355af1b038459ad20410e8e88256f767ebfc9b42 -size 26323 +oid sha256:3a61566b38636c7f66d4443ddf42330b2123eb4dbe5829df561e18c85a13dc59 +size 26245 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en.png index ef15138a1e..7463908c41 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e24080b2731014102634cea433fc357ab5cb76bfb149a266a61e345d1c718a28 -size 15875 +oid sha256:0b7570c4dc464557985e06a3d90fac595e5363f53aea02cce86dab76a58863d3 +size 15612 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en.png index 9d45f7b9c6..81b4f49cb3 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b0a79c489f22ae47dcdc8d212dcf2cea595d2922b816e1625f163be4085182c6 -size 20271 +oid sha256:822fe5f5e803509db19834d72fe7d1960cf0c8e0d53255f1b25bdae590277fd3 +size 19991 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en.png index f4f6854298..dac29e6155 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:907246ef2913f37b087e3bc96b8361b75e3a672854d613c7788b3dcf66393796 -size 25036 +oid sha256:ae41bb5c48e710ad31e37c78a1db6c928028ac52ac2d0a8e4574ebc75497f5b3 +size 24743 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en.png index d8498fd392..c1ac92f83a 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5e7576ea8ec6f504070c59997482135c4cd2e6d647b1b6f6570a0d23df73509 -size 42293 +oid sha256:6ba118648bb2587492b8381417b2ea7d125e715a1a047ae46feceb28f257af5c +size 56973 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en.png index 98b2583f91..7cdf882032 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9cabe4d952cff321039898fa130da7b92d9aafa96e9c0bc2e0a6f2a3f610031 -size 40136 +oid sha256:ca1abbdb9f265f6372e172a23024c667df4ee16897dcf5ba3d86891dae255dcd +size 54515 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en.png index af5d935a8d..c528154bfb 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3ea3591ecafd089a834c818551eaeb919f303cedb23106cc4295eb443273506 -size 37192 +oid sha256:e94bf908067fbea798749b67aae88f26867bc1633a73d0f6b96ab085a4ccbb6c +size 41080 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en.png index 2c3a18ad3d..510fccb59e 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c5a213813a4539a0a3d32960e7ec93f6972a5fe8e75d26037a82f0d9eaa47926 -size 47000 +oid sha256:febe2f1b89d81289ec248f61f3c0058136143dadc43763ebc074b23be36ad5ea +size 49305 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en.png index 738725584c..fa86c41af4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6aee0fb024f80729f7e39b89f638ce6d6b5e2af8f134b4745c523cebb542b04 -size 36918 +oid sha256:1210e30466e2561333261762640db7b29339034f6b9bd8b371ebde43d2118e7d +size 40788 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en.png index 6f3a1ce963..ff5cad9907 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c291e16eb5eb3c7853ada8c968291169c7e17ae8cde0f77b0a6d3f00c856e254 -size 46648 +oid sha256:aac26520f2e8e9ca823958191d4e51abc31072fb4cb1d2914b20a6ecc7456c71 +size 48964 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_0_en.png index 863a74a621..81446e80a5 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d45d0d34ceef4b91556957331591ef9e44b5f340cf202629a8641d4a575492f -size 78705 +oid sha256:604ac96927bdd49547fd39d2c9313ecd87b59ed7c8bbf9f5e5973cdb3f948b5e +size 82277 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_10_en.png index 788621482f..01655ac6c0 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:48176370d65f8bfb5633e4d476bae6aad9b0a2cfe644400cf520db42b8d69e5c -size 102709 +oid sha256:a2f1c32f860df198a084dc1570dde8f94cedf304d5f21036cfbc39c0dc43e603 +size 106169 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_1_en.png index 863a74a621..81446e80a5 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d45d0d34ceef4b91556957331591ef9e44b5f340cf202629a8641d4a575492f -size 78705 +oid sha256:604ac96927bdd49547fd39d2c9313ecd87b59ed7c8bbf9f5e5973cdb3f948b5e +size 82277 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_2_en.png index 7f58d6bdd0..bdff50ac76 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3567c1c190db20eb13c4eeab1c846f48ac2579d797d3f10ae773132050fd616f -size 79121 +oid sha256:3b73e6363372ac7e23fec07eaed7332cbd4436b57b58292fd02a1c6736470431 +size 82728 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_3_en.png index 5010844ada..81c3cd3b4c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:510c61d0970c692d6faff16f131d4186a2cbe7f3183e40399647b9e456965333 -size 22808 +oid sha256:ce301d1fa711ea2f00193cd808580da80cb3eb9024824aacb17e764e775986fa +size 61590 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_4_en.png index d953e66bf8..a8de70c034 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e812f3ed67b96b3809b268878d741f1e9471472e8d2272b5b96a34d9d19802e4 -size 22547 +oid sha256:611016bb64174fe744cc34b427c8ac6216a326cd3ffb514ab511615e4cffe4d2 +size 61343 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_5_en.png index d6b011dde3..522b5b859c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e268ec678b7cae5d0e58330334bdd196ae227b78da9326186355926b1fa5420f -size 20603 +oid sha256:c983fbda82828a169957e6d37b867e98eef6d2807707fd736b20b6a758317f21 +size 59593 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_6_en.png index 90ba5aa04e..bd0c3984d7 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f6cc1ef8d27973a246034ae6167343bf30e1362fa13d104b63a98b1fed82459 -size 97154 +oid sha256:9b5e578b50b6e5b4e06bd45cb16064ff425736ab1e5db12db8ffac1fc19a2d9f +size 100683 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_0_en.png index 532c483a2a..b80a127217 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c3693efe0d039fa086576d2c02bc65984783f040fa014d9f0c1a6124354cddd1 -size 86206 +oid sha256:80702a8d97a3d178f8d4b8c7cd91f34e5b2d05da5babcdb148d347820633df72 +size 88608 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_10_en.png index af64823144..d6fcf4c50b 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd96e88b68989c1b49d1d9028b9d23bd7f29019162578b339f34dffa53c0909b -size 109477 +oid sha256:212e99c8bf79e5465c73305b9941f5b99fe27feb0e98a0b436e72288e5d3f2f1 +size 111946 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_1_en.png index 532c483a2a..b80a127217 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c3693efe0d039fa086576d2c02bc65984783f040fa014d9f0c1a6124354cddd1 -size 86206 +oid sha256:80702a8d97a3d178f8d4b8c7cd91f34e5b2d05da5babcdb148d347820633df72 +size 88608 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_2_en.png index 847c69e8ef..d2b85d8dca 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a36a325a8f8cb470c18b4c123ad88f76e471a7488aa2581e1318a33f90706c46 -size 86977 +oid sha256:dd8ab022ccecdff2350e6d2f58c7437dfe8a587f5cda6276a415b6c737538d6c +size 88589 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_3_en.png index 3a1bcb5d3d..0993af003d 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c297102e973faa502a6567e1b3b08310f788961e92c05009a84ae5505aa1f6c4 -size 20809 +oid sha256:6649a87c37110a6820802a6fddf51250bd05f3b6b1ad4cfcf79bbcb20058d74e +size 70000 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_4_en.png index 1557f197a5..f02609d7dd 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e5c32de6f9cd766480cf5dac9f49dd5ad6083d48dd1901cdcd137377cf98dc7c -size 20581 +oid sha256:be1919432e860b977e841ce4d2d0bb3452e680a3575433707f4f9cef39860815 +size 69758 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_5_en.png index 6bddcdb30e..ad1d7b2e48 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:609178fe7f051bbfadf1859737b6a6241bd1a45c2053c2d5f72856a07f2f63b2 -size 18736 +oid sha256:22220f563256a794ea94d7e594c06e36ca26a0427ebcb8d1e838089453217af4 +size 67952 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_6_en.png index 2793182e9d..7cc568c6e6 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:880fdd1f31104aafc032a5abb192a49d897efb68b07add37b4939c7c5be78c24 -size 103608 +oid sha256:44520745df70fbcfb7a1e6cdc309dfc931e3afd55843431ba43b4e9aec21cbfc +size 106144 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItemSelectedTrailingContent_Multiple selection List item - selection in trailing content_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItemSelectedTrailingContent_Multiple_selection_List_item_-_selection_in_trailing_content_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItemSelectedTrailingContent_Multiple selection List item - selection in trailing content_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItemSelectedTrailingContent_Multiple_selection_List_item_-_selection_in_trailing_content_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple selection List item - selection in supporting text_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple_selection_List_item_-_selection_in_supporting_text_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple selection List item - selection in supporting text_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple_selection_List_item_-_selection_in_supporting_text_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItem_Multiple selection List item - no selection_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItem_Multiple_selection_List_item_-_no_selection_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItem_Multiple selection List item - no selection_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_MutipleSelectionListItem_Multiple_selection_List_item_-_no_selection_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemCustomFormattert_Single selection List item - custom formatter_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemCustomFormattert_Single_selection_List_item_-_custom_formatter_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemCustomFormattert_Single selection List item - custom formatter_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemCustomFormattert_Single_selection_List_item_-_custom_formatter_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemSelectedInSupportingText_Single selection List item - selection in supporting text_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemSelectedInSupportingText_Single_selection_List_item_-_selection_in_supporting_text_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemSelectedInSupportingText_Single selection List item - selection in supporting text_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemSelectedInSupportingText_Single_selection_List_item_-_selection_in_supporting_text_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemSelectedInTrailingContent_Single selection List item - selection in trailing content_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemSelectedInTrailingContent_Single_selection_List_item_-_selection_in_trailing_content_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemSelectedInTrailingContent_Single selection List item - selection in trailing content_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemSelectedInTrailingContent_Single_selection_List_item_-_selection_in_trailing_content_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemUnselectedWithSupportingText_Single selection List item - no selection, supporting text_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemUnselectedWithSupportingText_Single_selection_List_item_-_no_selection,_supporting_text_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemUnselectedWithSupportingText_Single selection List item - no selection, supporting text_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItemUnselectedWithSupportingText_Single_selection_List_item_-_no_selection,_supporting_text_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItem_Single selection List item - no selection_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItem_Single_selection_List_item_-_no_selection_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItem_Single selection List item - no selection_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_SingleSelectionListItem_Single_selection_List_item_-_no_selection_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItemEmpty_Text field List item - empty_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItemEmpty_Text_field_List_item_-_empty_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItemEmpty_Text field List item - empty_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItemEmpty_Text_field_List_item_-_empty_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text field List item - textfieldvalue_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text_field_List_item_-_textfieldvalue_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text field List item - textfieldvalue_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text_field_List_item_-_textfieldvalue_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItem_Text field List item - text_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItem_Text_field_List_item_-_text_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItem_Text field List item - text_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.list_TextFieldListItem_Text_field_List_item_-_text_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_Bloom_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_Bloom_Day_0_en.png index 4f8bcc5486..084aff9841 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_Bloom_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_Bloom_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b43889f07930a9dec3e2f08099963c9b59d3e9886380869f7b4f9bb8e5401a2e -size 65515 +oid sha256:77004b5d420cc5fe51f98b66033ed0d68cb9c308104fcdc281a0f06a6ece0fed +size 46964 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_Bloom_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_Bloom_Night_0_en.png index 32ca554ddb..601c55c1b3 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_Bloom_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_Bloom_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64bfd3b1c905e3061e809a8e6a2f1bcf9ac0541f79e8a1e5ecdb10ec9aab8454 -size 59443 +oid sha256:3467dc3bb6ed59048297d12badde6ab1748c19ff755809d4a14cf47e6c81ed9b +size 50973 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerDark_DateTime pickers_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerDark_DateTime pickers_en.png deleted file mode 100644 index b066d061a0..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerDark_DateTime pickers_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1d26d3a7f998e497105102f860e29762795fd4dcf91172cea1eae23c19bcdb05 -size 30604 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en.png new file mode 100644 index 0000000000..37b234801f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea4f3ed733159e0413383d8994943b5b7b3100160728138b7014a9567f665021 +size 30632 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerLight_DateTime pickers_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerLight_DateTime pickers_en.png deleted file mode 100644 index 88435161f2..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerLight_DateTime pickers_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:59e4995458c53d5156003bfc2ded6c30f2eb545390a47c7a490f5dc617e2937a -size 31019 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en.png new file mode 100644 index 0000000000..f957752384 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:657c14f3e42751aefbac5fc0b7d94462d2b90da1707ab7ce7997f5db1b29c6d2 +size 31013 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime pickers_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime pickers_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime pickers_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime pickers_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime pickers_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime pickers_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_CircularProgressIndicator_Progress Indicators_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_CircularProgressIndicator_Progress_Indicators_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_CircularProgressIndicator_Progress Indicators_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_CircularProgressIndicator_Progress_Indicators_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithDestructiveButton_Dialog with destructive button_Dialogs_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithDestructiveButton_Dialog_with_destructive_button_Dialogs_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithDestructiveButton_Dialog with destructive button_Dialogs_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithDestructiveButton_Dialog_with_destructive_button_Dialogs_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithOnlyMessageAndOkButton_Dialog with only message and ok button_Dialogs_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithOnlyMessageAndOkButton_Dialog_with_only_message_and_ok_button_Dialogs_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithOnlyMessageAndOkButton_Dialog with only message and ok button_Dialogs_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithOnlyMessageAndOkButton_Dialog_with_only_message_and_ok_button_Dialogs_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithThirdButton_Dialog with third button_Dialogs_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithThirdButton_Dialog_with_third_button_Dialogs_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithThirdButton_Dialog with third button_Dialogs_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithThirdButton_Dialog_with_third_button_Dialogs_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithTitleAndOkButton_Dialog with title and ok button_Dialogs_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithTitleAndOkButton_Dialog_with_title_and_ok_button_Dialogs_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithTitleAndOkButton_Dialog with title and ok button_Dialogs_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithTitleAndOkButton_Dialog_with_title_and_ok_button_Dialogs_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithTitleIconAndOkButton_Dialog with title, icon and ok button_Dialogs_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithTitleIconAndOkButton_Dialog_with_title,_icon_and_ok_button_Dialogs_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithTitleIconAndOkButton_Dialog with title, icon and ok button_Dialogs_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_DialogWithTitleIconAndOkButton_Dialog_with_title,_icon_and_ok_button_Dialogs_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_FloatingActionButton_Floating Action Buttons_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_FloatingActionButton_Floating_Action_Buttons_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_FloatingActionButton_Floating Action Buttons_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_FloatingActionButton_Floating_Action_Buttons_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_LinearProgressIndicator_Progress Indicators_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_LinearProgressIndicator_Progress_Indicators_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_LinearProgressIndicator_Progress Indicators_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_LinearProgressIndicator_Progress_Indicators_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List item - Primary action & Icon_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List_item_-_Primary_action_&_Icon_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List item - Primary action & Icon_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List_item_-_Primary_action_&_Icon_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineBothIcons_List item (1 line) - Both Icons_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineBothIcons_List_item_(1_line)_-_Both_Icons_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineBothIcons_List item (1 line) - Both Icons_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineBothIcons_List_item_(1_line)_-_Both_Icons_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingCheckbox_List item (1 line) - Leading Checkbox_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingCheckbox_List_item_(1_line)_-_Leading_Checkbox_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingCheckbox_List item (1 line) - Leading Checkbox_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingCheckbox_List_item_(1_line)_-_Leading_Checkbox_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingIcon_List item (1 line) - Leading Icon_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingIcon_List_item_(1_line)_-_Leading_Icon_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingIcon_List item (1 line) - Leading Icon_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingIcon_List_item_(1_line)_-_Leading_Icon_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingRadioButton_List item (1 line) - Leading RadioButton_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingRadioButton_List_item_(1_line)_-_Leading_RadioButton_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingRadioButton_List item (1 line) - Leading RadioButton_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingRadioButton_List_item_(1_line)_-_Leading_RadioButton_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingSwitch_List item (1 line) - Leading Switch_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingSwitch_List_item_(1_line)_-_Leading_Switch_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingSwitch_List item (1 line) - Leading Switch_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineLeadingSwitch_List_item_(1_line)_-_Leading_Switch_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineSimple_List item (1 line) - Simple_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineSimple_List_item_(1_line)_-_Simple_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineSimple_List item (1 line) - Simple_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineSimple_List_item_(1_line)_-_Simple_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingCheckBox_List item (1 line) - Trailing Checkbox_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingCheckBox_List_item_(1_line)_-_Trailing_Checkbox_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingCheckBox_List item (1 line) - Trailing Checkbox_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingCheckBox_List_item_(1_line)_-_Trailing_Checkbox_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingIcon_List item (1 line) - Trailing Icon_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingIcon_List_item_(1_line)_-_Trailing_Icon_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingIcon_List item (1 line) - Trailing Icon_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingIcon_List_item_(1_line)_-_Trailing_Icon_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingRadioButton_List item (1 line) - Trailing RadioButton_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingRadioButton_List_item_(1_line)_-_Trailing_RadioButton_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingRadioButton_List item (1 line) - Trailing RadioButton_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingRadioButton_List_item_(1_line)_-_Trailing_RadioButton_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingSwitch_List item (1 line) - Trailing Switch_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingSwitch_List_item_(1_line)_-_Trailing_Switch_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingSwitch_List item (1 line) - Trailing Switch_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemSingleLineTrailingSwitch_List_item_(1_line)_-_Trailing_Switch_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesBothIcons_List item (3 lines) - Both Icons_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesBothIcons_List_item_(3_lines)_-_Both_Icons_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesBothIcons_List item (3 lines) - Both Icons_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesBothIcons_List_item_(3_lines)_-_Both_Icons_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingCheckbox_List item (3 lines) - Leading Checkbox_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingCheckbox_List_item_(3_lines)_-_Leading_Checkbox_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingCheckbox_List item (3 lines) - Leading Checkbox_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingCheckbox_List_item_(3_lines)_-_Leading_Checkbox_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingIcon_List item (3 lines) - Leading Icon_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingIcon_List_item_(3_lines)_-_Leading_Icon_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingIcon_List item (3 lines) - Leading Icon_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingIcon_List_item_(3_lines)_-_Leading_Icon_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingRadioButton_List item (3 lines) - Leading RadioButton_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingRadioButton_List_item_(3_lines)_-_Leading_RadioButton_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingRadioButton_List item (3 lines) - Leading RadioButton_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingRadioButton_List_item_(3_lines)_-_Leading_RadioButton_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingSwitch_List item (3 lines) - Leading Switch_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingSwitch_List_item_(3_lines)_-_Leading_Switch_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingSwitch_List item (3 lines) - Leading Switch_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesLeadingSwitch_List_item_(3_lines)_-_Leading_Switch_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesSimple_List item (3 lines) - Simple_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesSimple_List_item_(3_lines)_-_Simple_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesSimple_List item (3 lines) - Simple_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesSimple_List_item_(3_lines)_-_Simple_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingCheckBox_List item (3 lines) - Trailing Checkbox_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingCheckBox_List_item_(3_lines)_-_Trailing_Checkbox_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingCheckBox_List item (3 lines) - Trailing Checkbox_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingCheckBox_List_item_(3_lines)_-_Trailing_Checkbox_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingIcon_List item (3 lines) - Trailing Icon_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingIcon_List_item_(3_lines)_-_Trailing_Icon_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingIcon_List item (3 lines) - Trailing Icon_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingIcon_List_item_(3_lines)_-_Trailing_Icon_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingRadioButton_List item (3 lines) - Trailing RadioButton_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingRadioButton_List_item_(3_lines)_-_Trailing_RadioButton_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingRadioButton_List item (3 lines) - Trailing RadioButton_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingRadioButton_List_item_(3_lines)_-_Trailing_RadioButton_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingSwitch_List item (3 lines) - Trailing Switch_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingSwitch_List_item_(3_lines)_-_Trailing_Switch_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingSwitch_List item (3 lines) - Trailing Switch_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemThreeLinesTrailingSwitch_List_item_(3_lines)_-_Trailing_Switch_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesBothIconsError_List item (2 lines) - Both Icons - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesBothIconsError_List_item_(2_lines)_-_Both_Icons_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesBothIconsError_List item (2 lines) - Both Icons - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesBothIconsError_List_item_(2_lines)_-_Both_Icons_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesBothIcons_List item (2 lines) - Both Icons_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesBothIcons_List_item_(2_lines)_-_Both_Icons_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesBothIcons_List item (2 lines) - Both Icons_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesBothIcons_List_item_(2_lines)_-_Both_Icons_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckboxError_List item (2 lines) - Leading Checkbox - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckboxError_List_item_(2_lines)_-_Leading_Checkbox_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckboxError_List item (2 lines) - Leading Checkbox - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckboxError_List_item_(2_lines)_-_Leading_Checkbox_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckbox_List item (2 lines) - Leading Checkbox_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckbox_List_item_(2_lines)_-_Leading_Checkbox_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckbox_List item (2 lines) - Leading Checkbox_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingCheckbox_List_item_(2_lines)_-_Leading_Checkbox_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingIconError_List item (2 lines) - Leading Icon - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingIconError_List_item_(2_lines)_-_Leading_Icon_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingIconError_List item (2 lines) - Leading Icon - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingIconError_List_item_(2_lines)_-_Leading_Icon_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingIcon_List item (2 lines) - Leading Icon_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingIcon_List_item_(2_lines)_-_Leading_Icon_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingIcon_List item (2 lines) - Leading Icon_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingIcon_List_item_(2_lines)_-_Leading_Icon_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButtonError_List item (2 lines) - Leading RadioButton - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButtonError_List_item_(2_lines)_-_Leading_RadioButton_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButtonError_List item (2 lines) - Leading RadioButton - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButtonError_List_item_(2_lines)_-_Leading_RadioButton_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButton_List item (2 lines) - Leading RadioButton_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButton_List_item_(2_lines)_-_Leading_RadioButton_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButton_List item (2 lines) - Leading RadioButton_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingRadioButton_List_item_(2_lines)_-_Leading_RadioButton_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitchError_List item (2 lines) - Leading Switch - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitchError_List_item_(2_lines)_-_Leading_Switch_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitchError_List item (2 lines) - Leading Switch - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitchError_List_item_(2_lines)_-_Leading_Switch_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitch_List item (2 lines) - Leading Switch_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitch_List_item_(2_lines)_-_Leading_Switch_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitch_List item (2 lines) - Leading Switch_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesLeadingSwitch_List_item_(2_lines)_-_Leading_Switch_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesSimpleError_List item (2 lines) - Simple - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesSimpleError_List_item_(2_lines)_-_Simple_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesSimpleError_List item (2 lines) - Simple - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesSimpleError_List_item_(2_lines)_-_Simple_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesSimple_List item (2 lines) - Simple_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesSimple_List_item_(2_lines)_-_Simple_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesSimple_List item (2 lines) - Simple_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesSimple_List_item_(2_lines)_-_Simple_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBoxError_List item (2 lines) - Trailing Checkbox - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBoxError_List_item_(2_lines)_-_Trailing_Checkbox_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBoxError_List item (2 lines) - Trailing Checkbox - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBoxError_List_item_(2_lines)_-_Trailing_Checkbox_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBox_List item (2 lines) - Trailing Checkbox_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBox_List_item_(2_lines)_-_Trailing_Checkbox_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBox_List item (2 lines) - Trailing Checkbox_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingCheckBox_List_item_(2_lines)_-_Trailing_Checkbox_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingIconError_List item (2 lines) - Trailing Icon - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingIconError_List_item_(2_lines)_-_Trailing_Icon_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingIconError_List item (2 lines) - Trailing Icon - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingIconError_List_item_(2_lines)_-_Trailing_Icon_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingIcon_List item (2 lines) - Trailing Icon_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingIcon_List_item_(2_lines)_-_Trailing_Icon_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingIcon_List item (2 lines) - Trailing Icon_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingIcon_List_item_(2_lines)_-_Trailing_Icon_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButtonError_List item (2 lines) - Trailing RadioButton - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButtonError_List_item_(2_lines)_-_Trailing_RadioButton_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButtonError_List item (2 lines) - Trailing RadioButton - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButtonError_List_item_(2_lines)_-_Trailing_RadioButton_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButton_List item (2 lines) - Trailing RadioButton_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButton_List_item_(2_lines)_-_Trailing_RadioButton_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButton_List item (2 lines) - Trailing RadioButton_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingRadioButton_List_item_(2_lines)_-_Trailing_RadioButton_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitchError_List item (2 lines) - Trailing Switch - Error_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitchError_List_item_(2_lines)_-_Trailing_Switch_-_Error_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitchError_List item (2 lines) - Trailing Switch - Error_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitchError_List_item_(2_lines)_-_Trailing_Switch_-_Error_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitch_List item (2 lines) - Trailing Switch_List items_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitch_List_item_(2_lines)_-_Trailing_Switch_List_items_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitch_List item (2 lines) - Trailing Switch_List items_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListItemTwoLinesTrailingSwitch_List_item_(2_lines)_-_Trailing_Switch_List_items_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDescriptionAndDivider_List section header with description and divider_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDescriptionAndDivider_List_section_header_with_description_and_divider_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDescriptionAndDivider_List section header with description and divider_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDescriptionAndDivider_List_section_header_with_description_and_divider_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDescription_List section header with description_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDescription_List_section_header_with_description_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDescription_List section header with description_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDescription_List_section_header_with_description_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDivider_List section header with divider_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDivider_List_section_header_with_divider_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDivider_List section header with divider_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeaderWithDivider_List_section_header_with_divider_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeader_List section header_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeader_List_section_header_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeader_List section header_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSectionHeader_List_section_header_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextCustomPadding_List supporting text - custom padding_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextCustomPadding_List_supporting_text_-_custom_padding_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextCustomPadding_List supporting text - custom padding_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextCustomPadding_List_supporting_text_-_custom_padding_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextDefaultPadding_List supporting text - default padding_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextDefaultPadding_List_supporting_text_-_default_padding_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextDefaultPadding_List supporting text - default padding_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextDefaultPadding_List_supporting_text_-_default_padding_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextLargePadding_List supporting text - large padding_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextLargePadding_List_supporting_text_-_large_padding_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextLargePadding_List supporting text - large padding_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextLargePadding_List_supporting_text_-_large_padding_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextNoPadding_List supporting text - no padding_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextNoPadding_List_supporting_text_-_no_padding_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextNoPadding_List supporting text - no padding_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextNoPadding_List_supporting_text_-_no_padding_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List supporting text - small padding_List sections_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List_supporting_text_-_small_padding_List_sections_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List supporting text - small padding_List sections_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List_supporting_text_-_small_padding_List_sections_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_MediumTopAppBar_App Bars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_MediumTopAppBar_App_Bars_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_MediumTopAppBar_App Bars_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_MediumTopAppBar_App_Bars_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom Sheets_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom Sheets_en.png deleted file mode 100644 index b7073fe2b5..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom Sheets_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:437177eaa4bdd53a403f0c742c4ccee8d90ac3aebe357b97c8308a02535d4ede -size 7416 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en.png new file mode 100644 index 0000000000..eba368322a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7dfe9390f76d961b136be0e8117c75b5617f292f92cbb06ec55075a081e2c97e +size 7143 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom Sheets_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom Sheets_en.png deleted file mode 100644 index b468064746..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom Sheets_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3f6e971231fb2aa0eb200880b01b66ef1de1c646cbf13a6c9887c5cff49f2472 -size 7738 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en.png new file mode 100644 index 0000000000..5b7d3bc913 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b51c31823790651708d61bf7653583c2b6bd2fadd5051d0a74c677f964fa9954 +size 7657 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveNoneQuery_Search views_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveNoneQuery_Search_views_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveNoneQuery_Search views_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveNoneQuery_Search_views_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithContent_Search views_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithContent_Search_views_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithContent_Search views_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithContent_Search_views_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search views_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search views_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search views_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search_views_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search views_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search_views_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search views_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search_views_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search views_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search_views_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarInactive_Search views_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarInactive_Search_views_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarInactive_Search views_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SearchBarInactive_Search_views_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar with action and close button_Snackbars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar_with_action_and_close_button_Snackbars_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar with action and close button_Snackbars_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar_with_action_and_close_button_Snackbars_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar with action and close button on new line_Snackbars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar_with_action_and_close_button_on_new_line_Snackbars_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar with action and close button on new line_Snackbars_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar_with_action_and_close_button_on_new_line_Snackbars_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar with action on new line_Snackbars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar_with_action_on_new_line_Snackbars_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar with action on new line_Snackbars_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar_with_action_on_new_line_Snackbars_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithAction_Snackbar with action_Snackbars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithAction_Snackbar_with_action_Snackbars_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithAction_Snackbar with action_Snackbars_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_SnackbarWithAction_Snackbar_with_action_Snackbars_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_TopAppBar_App Bars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_TopAppBar_App_Bars_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_TopAppBar_App Bars_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_TopAppBar_App_Bars_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en.png index 33edf8e59a..63108d9433 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f229349b73eb44932d3e5afd47cca8b456354b298272c0b20a67ce77fc1fe16 -size 13574 +oid sha256:1c3f8d8596a343d874f047bdad91e52206308066b6837271a3083b31f139ef49 +size 13526 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en.png index 68612c2a19..90de328262 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de9e050ea9073ded176f1202fcf2550251f2c42197549bccdd37a339227f0cbf -size 12377 +oid sha256:a86e5acab8589a935f1368134181edd47046096ed04a53c2d81e2ced733af0a9 +size 12116 From 153aa6eceaeb2062f490ce4ad478e89359b38104 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 Nov 2024 13:34:18 +0100 Subject: [PATCH 46/50] Update test. --- .../VerifySelfSessionPresenterTest.kt | 26 +++---------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt index bfdf27ee5d..a7c47182f8 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt @@ -121,28 +121,6 @@ class VerifySelfSessionPresenterTest { } } - @Test - fun `present - Handles startSasVerification`() = runTest { - val service = unverifiedSessionService( - startVerificationLambda = { }, - ) - val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() - assertThat(initialState.step).isEqualTo(Step.Initial(false)) - initialState.eventSink(VerifySelfSessionViewEvents.StartSasVerification) - // Await for other device response: - assertThat(awaitItem().step).isEqualTo(Step.AwaitingOtherDeviceResponse) - service.emitVerificationFlowState(VerificationFlowState.DidStartSasVerification) - // ChallengeReceived: - service.emitVerificationFlowState(VerificationFlowState.DidReceiveVerificationData(SessionVerificationData.Emojis(emptyList()))) - val verifyingState = awaitItem() - assertThat(verifyingState.step).isInstanceOf(Step.Verifying::class.java) - } - } - @Test fun `present - Cancellation on initial state does nothing`() = runTest { val presenter = createVerifySelfSessionPresenter( @@ -189,6 +167,7 @@ class VerifySelfSessionPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + awaitItem().eventSink(VerifySelfSessionViewEvents.UseAnotherDevice) awaitItem().eventSink(VerifySelfSessionViewEvents.RequestVerification) service.emitVerificationFlowState(VerificationFlowState.DidFail) assertThat(awaitItem().step).isInstanceOf(Step.AwaitingOtherDeviceResponse::class.java) @@ -414,6 +393,9 @@ class VerifySelfSessionPresenterTest { ): VerifySelfSessionState { var state = awaitItem() assertThat(state.step).isEqualTo(Step.Initial(false)) + state.eventSink(VerifySelfSessionViewEvents.UseAnotherDevice) + state = awaitItem() + assertThat(state.step).isEqualTo(Step.UseAnotherDevice) state.eventSink(VerifySelfSessionViewEvents.RequestVerification) // Await for other device response: fakeService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest) From fff7c04421d9ad2f3c350870357aecde233ac495 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 Nov 2024 13:36:36 +0100 Subject: [PATCH 47/50] Use `test` extension --- .../VerifySelfSessionPresenterTest.kt | 76 +++++-------------- 1 file changed, 19 insertions(+), 57 deletions(-) diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt index a7c47182f8..806a1dbdf1 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt @@ -7,10 +7,7 @@ package io.element.android.features.verifysession.impl.outgoing -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow import app.cash.turbine.ReceiveTurbine -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.logout.api.LogoutUseCase import io.element.android.features.logout.test.FakeLogoutUseCase @@ -33,6 +30,7 @@ import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value +import io.element.android.tests.testutils.test import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -48,9 +46,7 @@ class VerifySelfSessionPresenterTest { val presenter = createVerifySelfSessionPresenter( service = unverifiedSessionService(), ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { awaitItem().run { assertThat(step).isEqualTo(Step.Initial(false)) assertThat(displaySkipButton).isTrue() @@ -65,9 +61,7 @@ class VerifySelfSessionPresenterTest { service = unverifiedSessionService(), buildMeta = buildMeta, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { assertThat(awaitItem().displaySkipButton).isFalse() } } @@ -83,9 +77,7 @@ class VerifySelfSessionPresenterTest { emitRecoveryState(RecoveryState.INCOMPLETE) } ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { assertThat(awaitItem().step).isEqualTo(Step.Initial(true)) resetLambda.assertions().isCalledOnce().with(value(true)) } @@ -100,9 +92,7 @@ class VerifySelfSessionPresenterTest { emitRecoveryState(RecoveryState.INCOMPLETE) } ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { assertThat(awaitItem().step).isEqualTo(Step.Initial(canEnterRecoveryKey = true, isLastDevice = true)) } } @@ -114,9 +104,7 @@ class VerifySelfSessionPresenterTest { startVerificationLambda = { }, ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { requestVerificationAndAwaitVerifyingState(service) } } @@ -126,9 +114,7 @@ class VerifySelfSessionPresenterTest { val presenter = createVerifySelfSessionPresenter( service = unverifiedSessionService(), ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.step).isEqualTo(Step.Initial(false)) val eventSink = initialState.eventSink @@ -145,9 +131,7 @@ class VerifySelfSessionPresenterTest { approveVerificationLambda = { }, ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) state.eventSink(VerifySelfSessionViewEvents.ConfirmVerification) // Cancelling @@ -164,9 +148,7 @@ class VerifySelfSessionPresenterTest { requestVerificationLambda = { }, ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { awaitItem().eventSink(VerifySelfSessionViewEvents.UseAnotherDevice) awaitItem().eventSink(VerifySelfSessionViewEvents.RequestVerification) service.emitVerificationFlowState(VerificationFlowState.DidFail) @@ -183,9 +165,7 @@ class VerifySelfSessionPresenterTest { cancelVerificationLambda = { }, ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) state.eventSink(VerifySelfSessionViewEvents.Cancel) assertThat(awaitItem().step).isEqualTo(Step.Canceled) @@ -199,9 +179,7 @@ class VerifySelfSessionPresenterTest { startVerificationLambda = { }, ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { requestVerificationAndAwaitVerifyingState(service) service.emitVerificationFlowState(VerificationFlowState.DidReceiveVerificationData(SessionVerificationData.Emojis(emptyList()))) ensureAllEventsConsumed() @@ -215,9 +193,7 @@ class VerifySelfSessionPresenterTest { startVerificationLambda = { }, ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) service.emitVerificationFlowState(VerificationFlowState.DidCancel) assertThat(awaitItem().step).isEqualTo(Step.Canceled) @@ -235,9 +211,7 @@ class VerifySelfSessionPresenterTest { startVerificationLambda = { }, ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) service.emitVerificationFlowState(VerificationFlowState.DidCancel) assertThat(awaitItem().step).isEqualTo(Step.Canceled) @@ -259,9 +233,7 @@ class VerifySelfSessionPresenterTest { approveVerificationLambda = { }, ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val state = requestVerificationAndAwaitVerifyingState( service, SessionVerificationData.Emojis(emojis) @@ -286,9 +258,7 @@ class VerifySelfSessionPresenterTest { declineVerificationLambda = { }, ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) state.eventSink(VerifySelfSessionViewEvents.DeclineVerification) assertThat(awaitItem().step).isEqualTo( @@ -309,9 +279,7 @@ class VerifySelfSessionPresenterTest { startVerificationLambda = { }, ) val presenter = createVerifySelfSessionPresenter(service) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) state.eventSink(VerifySelfSessionViewEvents.SkipVerification) assertThat(awaitItem().step).isEqualTo(Step.Skipped) @@ -331,9 +299,7 @@ class VerifySelfSessionPresenterTest { service = service, showDeviceVerifiedScreen = true, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { assertThat(awaitItem().step).isEqualTo(Step.Completed) } } @@ -351,9 +317,7 @@ class VerifySelfSessionPresenterTest { service = service, showDeviceVerifiedScreen = false, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { skipItems(1) assertThat(awaitItem().step).isEqualTo(Step.Skipped) } @@ -373,9 +337,7 @@ class VerifySelfSessionPresenterTest { service, logoutUseCase = FakeLogoutUseCase(signOutLambda) ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { skipItems(1) val initialItem = awaitItem() initialItem.eventSink(VerifySelfSessionViewEvents.SignOut) From 5311cf28c9fbc6ff5446fed157e42f45cf11d0fb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 Nov 2024 15:12:10 +0100 Subject: [PATCH 48/50] Change action to only "Done" when there is a verification failure. --- .../outgoing/VerifySelfSessionStateMachine.kt | 3 --- .../impl/outgoing/VerifySelfSessionView.kt | 9 +++------ .../outgoing/VerifySelfSessionPresenterTest.kt | 18 ------------------ 3 files changed, 3 insertions(+), 27 deletions(-) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt index 09f18538d0..a67cc4d331 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt @@ -66,9 +66,6 @@ class VerifySelfSessionStateMachine @Inject constructor( } } inState { - on { _: Event.RequestVerification, state -> - state.override { State.RequestingVerification.andLogStateChange() } - } on { _: Event.Reset, state -> state.override { State.Initial.andLogStateChange() } } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt index 4351bb0bf2..afebffea47 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt @@ -290,14 +290,11 @@ private fun VerifySelfSessionBottomMenu( VerificationBottomMenu { Button( modifier = Modifier.fillMaxWidth(), - text = stringResource(R.string.screen_session_verification_positive_button_canceled), - onClick = { eventSink(VerifySelfSessionViewEvents.RequestVerification) }, - ) - TextButton( - modifier = Modifier.fillMaxWidth(), - text = stringResource(CommonStrings.action_cancel), + text = stringResource(CommonStrings.action_done), onClick = onCancelClick, ) + // Placeholder so the 1st button keeps its vertical position + Spacer(modifier = Modifier.height(40.dp)) } } is Step.Ready -> { diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt index 806a1dbdf1..d8fe2f671c 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt @@ -186,24 +186,6 @@ class VerifySelfSessionPresenterTest { } } - @Test - fun `present - Restart after cancellation returns to requesting verification`() = runTest { - val service = unverifiedSessionService( - requestVerificationLambda = { }, - startVerificationLambda = { }, - ) - val presenter = createVerifySelfSessionPresenter(service) - presenter.test { - val state = requestVerificationAndAwaitVerifyingState(service) - service.emitVerificationFlowState(VerificationFlowState.DidCancel) - assertThat(awaitItem().step).isEqualTo(Step.Canceled) - state.eventSink(VerifySelfSessionViewEvents.RequestVerification) - // Went back to requesting verification - assertThat(awaitItem().step).isEqualTo(Step.AwaitingOtherDeviceResponse) - cancelAndIgnoreRemainingEvents() - } - } - @Test fun `present - Go back after cancellation returns to initial state`() = runTest { val service = unverifiedSessionService( From a6fedbafbdbea7c37399fe67deb362a04097a5ad Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 15:25:55 +0100 Subject: [PATCH 49/50] Update dependency org.matrix.rustcomponents:sdk-android to v0.2.60 (#3827) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependency org.matrix.rustcomponents:sdk-android to v0.2.60 * Fix SDK API breaks: - Map new `QueueWedgeError` cases. - Add `MediaUploadOnSendQueue` feature flag enabled on debug and nightly builds: this will by used by `Timeline.send*` media functions fot its new `useSendQueue` parameter. --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jorge Martín --- gradle/libs.versions.toml | 2 +- .../libraries/featureflag/api/FeatureFlags.kt | 8 ++++ .../item/event/LocalEventSendState.kt | 4 ++ .../libraries/matrix/impl/RustMatrixClient.kt | 3 ++ .../matrix/impl/RustMatrixClientFactory.kt | 1 + .../matrix/impl/room/RustMatrixRoom.kt | 3 ++ .../matrix/impl/room/RustRoomFactory.kt | 3 ++ .../matrix/impl/timeline/RustTimeline.kt | 45 ++++++++++++------- .../item/event/EventTimelineItemMapper.kt | 6 +++ .../matrix/impl/RustMatrixClientTest.kt | 2 + 10 files changed, 61 insertions(+), 16 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 224dc9cec9..1177419d2e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -173,7 +173,7 @@ jsoup = "org.jsoup:jsoup:1.18.1" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0" timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.59" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.60" matrix_richtexteditor = { module = "io.element.android:wysiwyg", 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" } diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt index de7fe02b5d..490c12ebe0 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt @@ -9,6 +9,7 @@ package io.element.android.libraries.featureflag.api import io.element.android.appconfig.OnBoardingConfig import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.core.meta.BuildType /** * To enable or disable a FeatureFlags, change the `defaultValue` value. @@ -132,4 +133,11 @@ enum class FeatureFlags( defaultValue = { false }, isFinished = false, ), + MediaUploadOnSendQueue( + key = "feature.media_upload_through_send_queue", + title = "Media upload through send queue", + description = "Experimental support for treating media uploads as regular events, with an improved retry and cancellation implementation.", + defaultValue = { buildMeta -> buildMeta.buildType != BuildType.RELEASE }, + isFinished = false, + ), } 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 e6a0eae7ed..c95fe46741 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 @@ -34,6 +34,10 @@ sealed interface LocalEventSendState { */ val users: List ) : VerifiedUser + + data class InvalidMimeType(val mimeType: String) : Failed + + data object MissingMediaContent : Failed } data class Sent( diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index a10448959c..3165cef1bf 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -12,6 +12,7 @@ import io.element.android.libraries.androidutils.file.safeDelete import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.childScope +import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.ProgressCallback @@ -125,6 +126,7 @@ class RustMatrixClient( baseCacheDirectory: File, clock: SystemClock, timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory, + featureFlagService: FeatureFlagService, ) : MatrixClient { override val sessionId: UserId = UserId(client.userId()) override val deviceId: DeviceId = DeviceId(client.deviceId()) @@ -188,6 +190,7 @@ class RustMatrixClient( roomContentForwarder = RoomContentForwarder(innerRoomListService), roomSyncSubscriber = roomSyncSubscriber, timelineEventTypeFilterFactory = timelineEventTypeFilterFactory, + featureFlagService = featureFlagService, ) override val mediaLoader: MatrixMediaLoader = RustMediaLoader( diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt index c33ccdb543..c22cb84c4d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt @@ -82,6 +82,7 @@ class RustMatrixClientFactory @Inject constructor( baseCacheDirectory = cacheDirectory, clock = clock, timelineEventTypeFilterFactory = timelineEventTypeFilterFactory, + featureFlagService = featureFlagService, ).also { Timber.tag(it.toString()).d("Creating Client with access token '$anonymizedAccessToken' and refresh token '$anonymizedRefreshToken'") } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index a80d092145..8b72282cd5 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.impl.room import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.childScope import io.element.android.libraries.core.extensions.mapFailure +import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback @@ -103,6 +104,7 @@ class RustMatrixRoom( private val roomContentForwarder: RoomContentForwarder, private val roomSyncSubscriber: RoomSyncSubscriber, private val matrixRoomInfoMapper: MatrixRoomInfoMapper, + private val featureFlagService: FeatureFlagService, ) : MatrixRoom { override val roomId = RoomId(innerRoom.id()) @@ -700,6 +702,7 @@ class RustMatrixRoom( dispatcher = roomDispatcher, roomContentForwarder = roomContentForwarder, onNewSyncedEvent = onNewSyncedEvent, + featureFlagsService = featureFlagService, ) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt index e06424e723..1b9fe4115e 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt @@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.impl.room import androidx.collection.lruCache import io.element.android.appconfig.TimelineConfig import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId @@ -49,6 +50,7 @@ class RustRoomFactory( private val innerRoomListService: InnerRoomListService, private val roomSyncSubscriber: RoomSyncSubscriber, private val timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory, + private val featureFlagService: FeatureFlagService, ) { @OptIn(ExperimentalCoroutinesApi::class) private val dispatcher = dispatchers.io.limitedParallelism(1) @@ -117,6 +119,7 @@ class RustRoomFactory( roomContentForwarder = roomContentForwarder, roomSyncSubscriber = roomSyncSubscriber, matrixRoomInfoMapper = matrixRoomInfoMapper, + featureFlagService = featureFlagService, ) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt index 019f59bfaf..98f7ccfba1 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt @@ -7,6 +7,8 @@ package io.element.android.libraries.matrix.impl.timeline +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.core.RoomId @@ -85,6 +87,7 @@ class RustTimeline( private val coroutineScope: CoroutineScope, private val dispatcher: CoroutineDispatcher, private val roomContentForwarder: RoomContentForwarder, + private val featureFlagsService: FeatureFlagService, onNewSyncedEvent: () -> Unit, ) : Timeline { private val initLatch = CompletableDeferred() @@ -330,6 +333,7 @@ class RustTimeline( formattedCaption: String?, progressCallback: ProgressCallback?, ): Result { + val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue) return sendAttachment(listOfNotNull(file, thumbnailFile)) { inner.sendImage( url = file.path, @@ -339,7 +343,7 @@ class RustTimeline( formattedCaption = formattedCaption?.let { FormattedBody(body = it, format = MessageFormat.Html) }, - storeInCache = true, + useSendQueue = useSendQueue, progressWatcher = progressCallback?.toProgressWatcher() ) } @@ -353,6 +357,7 @@ class RustTimeline( formattedCaption: String?, progressCallback: ProgressCallback?, ): Result { + val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue) return sendAttachment(listOfNotNull(file, thumbnailFile)) { inner.sendVideo( url = file.path, @@ -362,13 +367,14 @@ class RustTimeline( formattedCaption = formattedCaption?.let { FormattedBody(body = it, format = MessageFormat.Html) }, - storeInCache = true, + useSendQueue = useSendQueue, progressWatcher = progressCallback?.toProgressWatcher() ) } } override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result { + val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue) return sendAttachment(listOf(file)) { inner.sendAudio( url = file.path, @@ -376,15 +382,21 @@ class RustTimeline( // Maybe allow a caption in the future? caption = null, formattedCaption = null, - storeInCache = true, + useSendQueue = useSendQueue, progressWatcher = progressCallback?.toProgressWatcher() ) } } override suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result { + val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue) return sendAttachment(listOf(file)) { - inner.sendFile(file.path, fileInfo.map(), false, progressCallback?.toProgressWatcher()) + inner.sendFile( + url = file.path, + fileInfo = fileInfo.map(), + useSendQueue = useSendQueue, + progressWatcher = progressCallback?.toProgressWatcher(), + ) } } @@ -491,17 +503,20 @@ class RustTimeline( audioInfo: AudioInfo, waveform: List, progressCallback: ProgressCallback?, - ): Result = sendAttachment(listOf(file)) { - inner.sendVoiceMessage( - url = file.path, - audioInfo = audioInfo.map(), - waveform = waveform.toMSC3246range(), - // Maybe allow a caption in the future? - caption = null, - formattedCaption = null, - storeInCache = true, - progressWatcher = progressCallback?.toProgressWatcher(), - ) + ): Result { + val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue) + return sendAttachment(listOf(file)) { + inner.sendVoiceMessage( + url = file.path, + audioInfo = audioInfo.map(), + waveform = waveform.toMSC3246range(), + // Maybe allow a caption in the future? + caption = null, + formattedCaption = null, + useSendQueue = useSendQueue, + progressWatcher = progressCallback?.toProgressWatcher(), + ) + } } private fun sendAttachment(files: List, handle: () -> SendAttachmentJoinHandle): Result { 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 cec54d3d20..f93d3fe982 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 @@ -100,6 +100,12 @@ fun RustEventSendState?.map(): LocalEventSendState? { LocalEventSendState.Failed.Unknown(queueWedgeError.msg) } } + is QueueWedgeError.InvalidMimeType -> { + LocalEventSendState.Failed.InvalidMimeType(queueWedgeError.mimeType) + } + is QueueWedgeError.MissingMediaContent -> { + LocalEventSendState.Failed.MissingMediaContent + } } } is RustEventSendState.Sent -> LocalEventSendState.Sent(EventId(eventId)) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientTest.kt index 36c96d2dfe..22fcad92bf 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientTest.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.matrix.impl import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustClient import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustSyncService import io.element.android.libraries.matrix.impl.room.FakeTimelineEventTypeFilterFactory @@ -46,5 +47,6 @@ class RustMatrixClientTest { baseCacheDirectory = File(""), clock = FakeSystemClock(), timelineEventTypeFilterFactory = FakeTimelineEventTypeFilterFactory(), + featureFlagService = FakeFeatureFlagService(), ) } From 88d04985cda184255da822f719c2293cc4c52b40 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 7 Nov 2024 14:28:54 +0000 Subject: [PATCH 50/50] Update screenshots --- ...ession.impl.incoming_IncomingVerificationView_Day_7_en.png | 4 ++-- ...sion.impl.incoming_IncomingVerificationView_Night_7_en.png | 4 ++-- ...ysession.impl.outgoing_VerifySelfSessionView_Day_13_en.png | 3 +++ ...fysession.impl.outgoing_VerifySelfSessionView_Day_1_en.png | 4 ++-- ...fysession.impl.outgoing_VerifySelfSessionView_Day_4_en.png | 4 ++-- ...ession.impl.outgoing_VerifySelfSessionView_Night_13_en.png | 3 +++ ...session.impl.outgoing_VerifySelfSessionView_Night_1_en.png | 4 ++-- ...session.impl.outgoing_VerifySelfSessionView_Night_4_en.png | 4 ++-- 8 files changed, 18 insertions(+), 12 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_13_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en.png index 0aa47d9aa9..1c316959ed 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bee1454db757b0897ae2113d3a11ed9adef0eb6bc52f3775edac56ed8533a88f -size 24076 +oid sha256:d3340a2d29e6c1d86f5ec5664179cae9501fcc95381311174d7d6b45b15af326 +size 24123 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en.png index 32fa8a3383..1c46f8eb53 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:59a23ec5e3086349d3382f006cf1bcb4121183c83ea8c749aa95e3160561aef1 -size 23524 +oid sha256:c6c7e8cdf40bdf018931635565ac649fed518140a047a71cf70cf63e9edf54d3 +size 23932 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_en.png new file mode 100644 index 0000000000..567462d90f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18dadaebe7a32aacde31afa0352a343913955b099ca4a07851e3ffe75e88b4d6 +size 31012 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en.png index 130cc1d84a..29667b296d 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2cc647747c41f4c2cf96a3425e8b279b39fd4e09e1441e5b5afcd260f79afaa1 -size 22714 +oid sha256:71b9f32b26b391ff3bf231ca9f364a157f535c1e6ff52ee3e3ead3630bb1b239 +size 30007 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en.png index 4e84bcb0c2..1faa04af50 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a7fe45575eb8423161d355d9b99ecff4acc0f708e5b106d9d38aa2657942d736 -size 28219 +oid sha256:000374157cb5fbf6670b4af041fc385538df78bf4aecaf83ea24d39dfec84f23 +size 24278 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_13_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_13_en.png new file mode 100644 index 0000000000..bebfa94f6d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_13_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4dd274f8c2ade6213a13a47400ec3a571844eafcc82b292b1c813bd9aa098236 +size 30241 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en.png index 7e3a113610..af8d7eadbe 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:786a63d0d824657270b44428a1a251d0d521e6a97a4239516ae5f2eff06fdc3b -size 22125 +oid sha256:994b39ba25e011cce97504a1f3d4ed0c420b7bce87daafae77ba481cc629cdae +size 29051 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en.png index 76173e1b2d..d9b8a3a0de 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c29d3f5bbff739d23800345feb42e73fe9374c5cb5db27690278fb22c5546fe3 -size 27754 +oid sha256:3a3f08002e805fe5f7a4c96aa4b73c2fcd6e8b79e6a9e82bcc7bd3df50d8c22d +size 24015