From 1765398eb1f6fef26b4fc865fe0a4f18aefcc5d4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 11 May 2023 16:36:46 +0200 Subject: [PATCH 01/18] No need to duplicate the workflow, the workflow can have several `on` sections. --- .github/workflows/nightly.yml | 1 + .github/workflows/nightly_manual.yml | 43 ---------------------------- 2 files changed, 1 insertion(+), 43 deletions(-) delete mode 100644 .github/workflows/nightly_manual.yml diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 2f7c10a3d0..7d240080b0 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,6 +1,7 @@ name: Build and release nightly APK on: + workflow_dispatch: schedule: # Every nights at 4 - cron: "0 4 * * *" diff --git a/.github/workflows/nightly_manual.yml b/.github/workflows/nightly_manual.yml deleted file mode 100644 index 6e8ef7c684..0000000000 --- a/.github/workflows/nightly_manual.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Build and release nightly APK manually - -on: - workflow_dispatch - -env: - GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx3072m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.daemon.jvm.options="-Xmx2560m" -Dkotlin.incremental=false - CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 2 --no-daemon - -jobs: - nightly: - name: Build and publish nightly APK to Firebase - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Use JDK 17 - uses: actions/setup-java@v3 - with: - distribution: 'temurin' # See 'Supported distributions' for available options - java-version: '17' - - name: Install towncrier - run: | - python3 -m pip install towncrier - - name: Prepare changelog file - run: | - mv towncrier.toml towncrier.toml.bak - sed 's/CHANGES\.md/CHANGES_NIGHTLY\.md/' towncrier.toml.bak > towncrier.toml - rm towncrier.toml.bak - yes n | towncrier build --version nightly - - name: Build and upload Nightly APK - run: | - ./gradlew assembleNightly appDistributionUploadNightly $CI_GRADLE_ARG_PROPERTIES - env: - ELEMENT_ANDROID_NIGHTLY_KEYID: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_KEYID }} - ELEMENT_ANDROID_NIGHTLY_KEYPASSWORD: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_KEYPASSWORD }} - ELEMENT_ANDROID_NIGHTLY_STOREPASSWORD: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_STOREPASSWORD }} - FIREBASE_TOKEN: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_FIREBASE_TOKEN }} - - name: Additionally upload Nightly APK to browserstack for testing - continue-on-error: true # don't block anything by this upload failing (for now) - run: curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_PASSWORD" -X POST "https://api-cloud.browserstack.com/app-automate/upload" -F "file=@app/build/outputs/apk/nightly/app-universal-nightly.apk" -F "custom_id=element-x-android-nightly" - env: - BROWSERSTACK_USERNAME: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_USERNAME }} - BROWSERSTACK_PASSWORD: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_ACCESS_KEY }} From 89b9db3be6709a858a46311e266d1e962cdceff5 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Thu, 11 May 2023 17:56:13 +0200 Subject: [PATCH 02/18] [Media upload] Upload image, video and files (#411) * Add media upload * Display media upload error messages using a Snackbar. --- .../messages/impl/MessagesPresenter.kt | 6 + .../features/messages/impl/MessagesState.kt | 2 + .../messages/impl/MessagesStateProvider.kt | 1 + .../features/messages/impl/MessagesView.kt | 15 +- .../textcomposer/MessageComposerPresenter.kt | 55 +++++- .../messages/MessagesPresenterTest.kt | 3 + .../MessageComposerPresenterTest.kt | 172 ++++++++++++++---- gradle/libs.versions.toml | 1 + .../libraries/androidutils/file/File.kt | 5 +- .../libraries/core/mimetype/MimeTypes.kt | 1 + .../libraries/matrix/api/media/AudioInfo.kt | 3 +- .../libraries/matrix/api/room/MatrixRoom.kt | 13 ++ .../libraries/matrix/impl/media/AudioInfo.kt | 9 +- .../libraries/matrix/impl/media/FileInfo.kt | 7 + .../libraries/matrix/impl/media/ImageInfo.kt | 11 ++ .../matrix/impl/media/ThumbnailInfo.kt | 7 + .../libraries/matrix/impl/media/VideoInfo.kt | 11 ++ .../matrix/impl/room/RustMatrixRoom.kt | 30 +++ .../matrix/test/room/FakeMatrixRoom.kt | 20 ++ .../mediaupload/api/MediaUploadInfo.kt | 5 +- libraries/mediaupload/impl/build.gradle.kts | 1 + .../libraries/mediaupload/ImageCompressor.kt | 13 +- .../mediaupload/MediaPreProcessorImpl.kt | 57 +++--- .../libraries/mediaupload/VideoCompressor.kt | 2 +- 24 files changed, 373 insertions(+), 77 deletions(-) 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 4b0a6b2238..13f36c2ab6 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 @@ -41,6 +41,8 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.textcomposer.MessageComposerMode import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkStatus +import io.element.android.libraries.designsystem.utils.SnackbarDispatcher +import io.element.android.libraries.designsystem.utils.handleSnackbarMessage import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import timber.log.Timber @@ -52,6 +54,7 @@ class MessagesPresenter @Inject constructor( private val timelinePresenter: TimelinePresenter, private val actionListPresenter: ActionListPresenter, private val networkMonitor: NetworkMonitor, + private val snackbarDispatcher: SnackbarDispatcher, ) : Presenter { @Composable @@ -71,6 +74,8 @@ class MessagesPresenter @Inject constructor( val networkConnectionStatus by networkMonitor.connectivity.collectAsState(initial = networkMonitor.currentConnectivityStatus) + val snackbarMessage = handleSnackbarMessage(snackbarDispatcher) + LaunchedEffect(syncUpdateFlow) { roomAvatar.value = AvatarData( @@ -97,6 +102,7 @@ class MessagesPresenter @Inject constructor( timelineState = timelineState, actionListState = actionListState, hasNetworkConnection = networkConnectionStatus == NetworkStatus.Online, + snackbarMessage = snackbarMessage, eventSink = ::handleEvents ) } 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 88b25dd2d6..34e5f664ea 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 @@ -21,6 +21,7 @@ import io.element.android.features.messages.impl.actionlist.ActionListState import io.element.android.features.messages.impl.textcomposer.MessageComposerState import io.element.android.features.messages.impl.timeline.TimelineState import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.designsystem.utils.SnackbarMessage import io.element.android.libraries.matrix.api.core.RoomId @Immutable @@ -32,5 +33,6 @@ data class MessagesState( val timelineState: TimelineState, val actionListState: ActionListState, val hasNetworkConnection: Boolean, + val snackbarMessage: SnackbarMessage?, val eventSink: (MessagesEvents) -> Unit ) 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 8426f79721..9249b808a1 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 @@ -52,5 +52,6 @@ fun aMessagesState() = MessagesState( ), actionListState = anActionListState(), hasNetworkConnection = true, + snackbarMessage = null, 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 5bf26d88bc..cd70dc97cd 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 @@ -46,6 +46,7 @@ import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.SideEffect import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment @@ -94,7 +95,6 @@ fun MessagesView( val itemActionsBottomSheetState = rememberModalBottomSheetState( initialValue = ModalBottomSheetValue.Hidden, ) - val snackbarHostState = remember { SnackbarHostState() } val composerState = state.composerState val initialBottomSheetState = if (LocalInspectionMode.current && composerState.attachmentSourcePicker != null) { ModalBottomSheetValue.Expanded @@ -110,6 +110,19 @@ fun MessagesView( } } + val snackbarHostState = remember { SnackbarHostState() } + val snackbarMessageText = state.snackbarMessage?.let { stringResource(it.messageResId) } + if (snackbarMessageText != null) { + SideEffect { + coroutineScope.launch { + snackbarHostState.showSnackbar( + message = snackbarMessageText, + duration = state.snackbarMessage.duration + ) + } + } + } + // This is needed because the composer is inside an AndroidView that can't be affected by the FocusManager in Compose val localView = LocalView.current diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenter.kt index 8193c0c0b2..99d0f98b66 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenter.kt @@ -32,6 +32,8 @@ import io.element.android.libraries.core.data.toStableCharSequence import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo +import io.element.android.libraries.designsystem.utils.SnackbarDispatcher +import io.element.android.libraries.designsystem.utils.SnackbarMessage import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.SingleIn import io.element.android.libraries.featureflag.api.FeatureFlagService @@ -40,11 +42,13 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.mediapickers.api.PickerProvider import io.element.android.libraries.mediaupload.api.MediaPreProcessor import io.element.android.libraries.mediaupload.api.MediaType +import io.element.android.libraries.mediaupload.api.MediaUploadInfo import io.element.android.libraries.textcomposer.MessageComposerMode import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject +import io.element.android.libraries.ui.strings.R as StringR @SingleIn(RoomScope::class) class MessageComposerPresenter @Inject constructor( @@ -53,6 +57,7 @@ class MessageComposerPresenter @Inject constructor( private val mediaPickerProvider: PickerProvider, private val featureFlagService: FeatureFlagService, private val mediaPreProcessor: MediaPreProcessor, + private val snackbarDispatcher: SnackbarDispatcher, ) : Presenter { @Composable @@ -60,6 +65,7 @@ class MessageComposerPresenter @Inject constructor( val localCoroutineScope = rememberCoroutineScope() val galleryMediaPicker = mediaPickerProvider.registerGalleryPicker(onResult = { uri, mimeType -> + if (uri == null) return@registerGalleryPicker Timber.d("Media picked from $uri") // We don't know which type of media was retrieved, so we need this check val mediaType = when { @@ -67,22 +73,25 @@ class MessageComposerPresenter @Inject constructor( mimeType.isMimeTypeVideo() -> MediaType.Video else -> error("MimeType must be either image/* or video/*") } - localCoroutineScope.handleMediaPreProcessing(uri, mediaType) + appCoroutineScope.sendMedia(uri, mediaType) }) val filesPicker = mediaPickerProvider.registerFilePicker(mimeType = MimeTypes.Any) { uri -> + if (uri == null) return@registerFilePicker Timber.d("File picked from $uri") - localCoroutineScope.handleMediaPreProcessing(uri, MediaType.File) + appCoroutineScope.sendMedia(uri, MediaType.File) } val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker { uri -> + if (uri == null) return@registerCameraPhotoPicker Timber.d("Photo saved at $uri") - localCoroutineScope.handleMediaPreProcessing(uri, MediaType.Image, deleteOriginal = true) + appCoroutineScope.sendMedia(uri, MediaType.Image, deleteOriginal = true) } val cameraVideoPicker = mediaPickerProvider.registerCameraVideoPicker { uri -> + if (uri == null) return@registerCameraVideoPicker Timber.d("Video saved at $uri") - localCoroutineScope.handleMediaPreProcessing(uri, MediaType.Video, deleteOriginal = true) + appCoroutineScope.sendMedia(uri, MediaType.Video, deleteOriginal = true) } val isFullScreen = rememberSaveable { @@ -181,14 +190,44 @@ class MessageComposerPresenter @Inject constructor( } } - private fun CoroutineScope.handleMediaPreProcessing( - uri: Uri?, + private fun CoroutineScope.sendMedia( + uri: Uri, mediaType: MediaType, deleteOriginal: Boolean = false ) = launch { - if (uri == null) return@launch + runCatching { + val info = handleMediaPreProcessing(uri, mediaType, deleteOriginal).getOrNull() ?: return@runCatching + when (info) { + is MediaUploadInfo.Image -> { + room.sendImage(info.file, info.thumbnailInfo.file, info.info) + } - val result = mediaPreProcessor.process(uri, mediaType, deleteOriginal = deleteOriginal) + is MediaUploadInfo.Video -> { + room.sendVideo(info.file, info.thumbnailInfo.file, info.info) + } + + is MediaUploadInfo.AnyFile -> { + room.sendFile(info.file, info.info) + } + else -> error("Unexpected MediaUploadInfo format: $info") + }.getOrThrow() + }.onFailure { + snackbarDispatcher.post(SnackbarMessage(StringR.string.screen_media_upload_preview_error_failed_sending)) + Timber.e(it, "Couldn't upload media") + }.onSuccess { + Timber.d("Media uploaded") + } + } + + private suspend fun handleMediaPreProcessing( + uri: Uri, + mediaType: MediaType, + deleteOriginal: Boolean, + ): Result { + val result = mediaPreProcessor.process(uri, mediaType, deleteOriginal = deleteOriginal) Timber.d("Pre-processed media result: $result") + return result.onFailure { + snackbarDispatcher.post(SnackbarMessage(StringR.string.screen_media_upload_preview_error_failed_processing)) + } } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index d2ba50ad5a..6756f55f9c 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -31,6 +31,7 @@ import io.element.android.features.messages.impl.actionlist.model.TimelineItemAc import io.element.android.features.messages.impl.textcomposer.MessageComposerPresenter import io.element.android.features.messages.impl.timeline.TimelinePresenter import io.element.android.features.networkmonitor.test.FakeNetworkMonitor +import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.test.AN_EVENT_ID @@ -136,6 +137,7 @@ class MessagesPresenterTest { mediaPickerProvider = FakePickerProvider(), featureFlagService = FakeFeatureFlagService(), mediaPreProcessor = FakeMediaPreProcessor(), + snackbarDispatcher = SnackbarDispatcher(), ) val timelinePresenter = TimelinePresenter( timelineItemsFactory = aTimelineItemsFactory(), @@ -148,6 +150,7 @@ class MessagesPresenterTest { timelinePresenter = timelinePresenter, actionListPresenter = actionListPresenter, networkMonitor = FakeNetworkMonitor(), + snackbarDispatcher = SnackbarDispatcher(), ) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt index dc1efd7d88..2ef7aa0c69 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt @@ -29,9 +29,13 @@ import io.element.android.features.messages.impl.textcomposer.MessageComposerPre import io.element.android.features.messages.impl.textcomposer.MessageComposerState import io.element.android.libraries.core.data.StableCharSequence import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService +import io.element.android.libraries.matrix.api.media.ImageInfo +import io.element.android.libraries.matrix.api.media.ThumbnailInfo +import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.test.ANOTHER_MESSAGE import io.element.android.libraries.matrix.test.AN_EVENT_ID @@ -42,14 +46,22 @@ import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.mediapickers.api.PickerProvider import io.element.android.libraries.mediapickers.test.FakePickerProvider import io.element.android.libraries.mediaupload.api.MediaPreProcessor +import io.element.android.libraries.mediaupload.api.MediaUploadInfo +import io.element.android.libraries.mediaupload.api.ThumbnailProcessingInfo import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor import io.element.android.libraries.textcomposer.MessageComposerMode import io.mockk.mockk import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.android.awaitFrame +import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain import org.junit.Test +import java.io.File class MessageComposerPresenterTest { @@ -62,6 +74,7 @@ class MessageComposerPresenterTest { } } private val mediaPreProcessor = FakeMediaPreProcessor() + private val snackbarDispatcher = SnackbarDispatcher() @Test fun `present - initial state`() = runTest { @@ -285,16 +298,83 @@ class MessageComposerPresenterTest { } @Test - fun `present - Pick media from gallery`() = runTest { - val presenter = createPresenter(this) + fun `present - Pick image from gallery`() = runTest { + val room = FakeMatrixRoom() + val presenter = createPresenter(this, room = room) pickerProvider.givenMimeType(MimeTypes.Images) + mediaPreProcessor.givenResult(Result.success( + MediaUploadInfo.Image( + file = File("/some/path"), + info = ImageInfo( + width = null, + height = null, + mimetype = null, + size = null, + thumbnailInfo = null, + thumbnailUrl = null, + blurhash = null, + ), + thumbnailInfo = ThumbnailProcessingInfo( + file = File("/some/path"), + info = ThumbnailInfo( + width = null, + height = null, + mimetype = null, + size = null, + ), + blurhash = "", + ) + ) + )) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { val initialState = awaitItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) + // Wait for the launched upload coroutine to run + runCurrent() + assertThat(room.sendMediaCount).isEqualTo(1) + } + } - // TODO verify some post processing of the selected media is done + @Test + fun `present - Pick video from gallery`() = runTest { + val room = FakeMatrixRoom() + val presenter = createPresenter(this, room = room) + pickerProvider.givenMimeType(MimeTypes.Videos) + mediaPreProcessor.givenResult(Result.success( + MediaUploadInfo.Video( + file = File("/some/path"), + info = VideoInfo( + width = null, + height = null, + mimetype = null, + duration = null, + size = null, + thumbnailInfo = null, + thumbnailUrl = null, + blurhash = null, + ), + thumbnailInfo = ThumbnailProcessingInfo( + file = File("/some/path"), + info = ThumbnailInfo( + width = null, + height = null, + mimetype = null, + size = null, + ), + blurhash = "", + ) + ) + )) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) + // Wait for the launched upload coroutine to run + runCurrent() + assertThat(room.sendMediaCount).isEqualTo(1) } } @@ -329,40 +409,67 @@ class MessageComposerPresenterTest { @Test fun `present - Pick file from storage`() = runTest { - val presenter = createPresenter(this) + val room = FakeMatrixRoom() + val presenter = createPresenter(this, room = room) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) + // Wait for the launched upload coroutine to run + runCurrent() + assertThat(room.sendMediaCount).isEqualTo(1) + } + } + + @Test + fun `present - Take photo`() = runTest { + val room = FakeMatrixRoom() + val presenter = createPresenter(this, room = room) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(MessageComposerEvents.PickCameraAttachmentSource.Photo) + // Wait for the launched upload coroutine to run + runCurrent() + assertThat(room.sendMediaCount).isEqualTo(1) + } + } + + @Test + fun `present - Record video`() = runTest { + val room = FakeMatrixRoom() + val presenter = createPresenter(this, room = room) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(MessageComposerEvents.PickCameraAttachmentSource.Video) + // Wait for the launched upload coroutine to run + runCurrent() + assertThat(room.sendMediaCount).isEqualTo(1) + } + } + + @Test + fun `present - Uploading media failure can be recovered from`() = runTest { + val room = FakeMatrixRoom().apply { + givenSendMediaResult(Result.failure(Exception())) + } + val presenter = createPresenter(this, room = room) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { val initialState = awaitItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) - // TODO verify some post processing of the selected media is done - } - } - - @Test - fun `present - Take photo`() = runTest { - val presenter = createPresenter(this) - moleculeFlow(RecompositionClock.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() - initialState.eventSink(MessageComposerEvents.PickCameraAttachmentSource.Photo) - - // TODO verify some post processing of the captured image is done - } - } - - @Test - fun `present - Record video`() = runTest { - val presenter = createPresenter(this) - moleculeFlow(RecompositionClock.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() - initialState.eventSink(MessageComposerEvents.PickCameraAttachmentSource.Video) - - // TODO verify some post processing of the captured video is done + snackbarDispatcher.snackbarMessage.test { + // Initial value is always null + skipItems(1) + // Assert error message received + assertThat(awaitItem()).isNotNull() + } } } @@ -381,8 +488,9 @@ class MessageComposerPresenterTest { pickerProvider: PickerProvider = this.pickerProvider, featureFlagService: FeatureFlagService = this.featureFlagService, mediaPreProcessor: MediaPreProcessor = this.mediaPreProcessor, + snackbarDispatcher: SnackbarDispatcher = this.snackbarDispatcher, ) = MessageComposerPresenter( - coroutineScope, room, pickerProvider, featureFlagService, mediaPreProcessor + coroutineScope, room, pickerProvider, featureFlagService, mediaPreProcessor, snackbarDispatcher ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a6e923667e..3504a3b0fe 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -138,6 +138,7 @@ sqlite = "androidx.sqlite:sqlite:2.3.1" unifiedpush = "com.github.UnifiedPush:android-connector:2.1.1" gujun_span = "me.gujun.android:span:1.7" otaliastudios_transcoder = "com.otaliastudios:transcoder:0.10.5" +vanniktech_blurhash = "com.vanniktech:blurhash:0.1.0" # Di inject = "javax.inject:javax.inject:1" diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/file/File.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/file/File.kt index 137b15a893..80df69cbcc 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/file/File.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/file/File.kt @@ -37,6 +37,7 @@ fun File.safeDelete() { ) } -suspend fun Context.createTmpFile(baseDir: File = cacheDir): File = withContext(Dispatchers.IO) { - File.createTempFile(UUID.randomUUID().toString(), null, baseDir).apply { mkdirs() } +suspend fun Context.createTmpFile(baseDir: File = cacheDir, extension: String? = null): File = withContext(Dispatchers.IO) { + val suffix = extension?.let { ".$extension" } + File.createTempFile(UUID.randomUUID().toString(), suffix, baseDir).apply { mkdirs() } } diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt index 3e3afbfe0e..20e2a6d8ec 100644 --- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt @@ -32,6 +32,7 @@ object MimeTypes { const val Gif = "image/gif" const val Videos = "video/*" + const val Mp4 = "video/mp4" const val Audio = "audio/*" diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioInfo.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioInfo.kt index 6547f32448..6ed6e474b6 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioInfo.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioInfo.kt @@ -18,5 +18,6 @@ package io.element.android.libraries.matrix.api.media data class AudioInfo( val duration: Long?, - val size: Long? + val size: Long?, + val mimeType: String?, ) 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 35451874aa..90b55a3a42 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 @@ -20,10 +20,15 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId +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.matrix.api.timeline.MatrixTimeline import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import java.io.Closeable +import java.io.File interface MatrixRoom : Closeable { val sessionId: SessionId @@ -67,6 +72,14 @@ interface MatrixRoom : Closeable { suspend fun redactEvent(eventId: EventId, reason: String? = null): Result + suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo): Result + + suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo): Result + + suspend fun sendAudio(file: File, audioInfo: AudioInfo): Result + + suspend fun sendFile(file: File, fileInfo: FileInfo): Result + suspend fun leave(): Result suspend fun acceptInvitation(): Result diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioInfo.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioInfo.kt index 1108dd1cc8..7c35c14fb7 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioInfo.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioInfo.kt @@ -21,5 +21,12 @@ import org.matrix.rustcomponents.sdk.AudioInfo as RustAudioInfo fun RustAudioInfo.map(): AudioInfo = AudioInfo( duration = duration?.toLong(), - size = size?.toLong() + size = size?.toLong(), + mimeType = mimetype +) + +fun AudioInfo.map(): RustAudioInfo = RustAudioInfo( + duration = duration?.toULong(), + size = size?.toULong(), + mimetype = mimeType, ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/FileInfo.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/FileInfo.kt index 98c96c4d9a..a13c48efc5 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/FileInfo.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/FileInfo.kt @@ -27,3 +27,10 @@ fun RustFileInfo.map(): FileInfo = FileInfo( thumbnailInfo = thumbnailInfo?.map(), thumbnailUrl = thumbnailSource?.useUrl() ) + +fun FileInfo.map(): RustFileInfo = RustFileInfo( + mimetype = mimetype, + size = size?.toULong(), + thumbnailInfo = thumbnailInfo?.map(), + thumbnailSource = null +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt index 27ab6d656a..21130ab86e 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt @@ -17,6 +17,7 @@ package io.element.android.libraries.matrix.impl.media import io.element.android.libraries.matrix.api.media.ImageInfo +import org.matrix.rustcomponents.sdk.MediaSource import org.matrix.rustcomponents.sdk.ImageInfo as RustImageInfo fun RustImageInfo.map(): ImageInfo = ImageInfo( @@ -28,3 +29,13 @@ fun RustImageInfo.map(): ImageInfo = ImageInfo( thumbnailUrl = thumbnailSource?.useUrl(), blurhash = blurhash ) + +fun ImageInfo.map(): RustImageInfo = RustImageInfo( + height = height?.toULong(), + width = width?.toULong(), + mimetype = mimetype, + size = size?.toULong(), + thumbnailInfo = thumbnailInfo?.map(), + thumbnailSource = null, + blurhash = blurhash, +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ThumbnailInfo.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ThumbnailInfo.kt index 3f10720132..c3940ac967 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ThumbnailInfo.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ThumbnailInfo.kt @@ -25,3 +25,10 @@ fun RustThumbnailInfo.map(): ThumbnailInfo = ThumbnailInfo( mimetype = mimetype, size = size?.toLong() ) + +fun ThumbnailInfo.map(): RustThumbnailInfo = RustThumbnailInfo( + height = height?.toULong(), + width = width?.toULong(), + mimetype = mimetype, + size = size?.toULong() +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/VideoInfo.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/VideoInfo.kt index 0db364f544..9d03e2be2f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/VideoInfo.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/VideoInfo.kt @@ -29,3 +29,14 @@ fun RustVideoInfo.map(): VideoInfo = VideoInfo( thumbnailUrl = thumbnailSource?.useUrl(), blurhash = blurhash ) + +fun VideoInfo.map(): RustVideoInfo = RustVideoInfo( + duration = duration?.toULong(), + height = height?.toULong(), + width = width?.toULong(), + mimetype = mimetype, + size = size?.toULong(), + thumbnailInfo = thumbnailInfo?.map(), + thumbnailSource = null, + blurhash = blurhash +) 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 062f24ab62..3930ebebe6 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 @@ -21,10 +21,15 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId +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.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.matrix.api.timeline.MatrixTimeline +import io.element.android.libraries.matrix.impl.media.map import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -39,6 +44,7 @@ import org.matrix.rustcomponents.sdk.SlidingSyncRoom import org.matrix.rustcomponents.sdk.UpdateSummary import org.matrix.rustcomponents.sdk.genTransactionId import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown +import java.io.File class RustMatrixRoom( override val sessionId: SessionId, @@ -202,4 +208,28 @@ class RustMatrixRoom( } } + override suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo): Result { + return runCatching { + innerRoom.sendImage(file.path, thumbnailFile.path, imageInfo.map()) + } + } + + override suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo): Result { + return runCatching { + innerRoom.sendVideo(file.path, thumbnailFile.path, videoInfo.map()) + } + } + + override suspend fun sendAudio(file: File, audioInfo: AudioInfo): Result { + return runCatching { + innerRoom.sendAudio(file.path, audioInfo.map()) + } + } + + override suspend fun sendFile(file: File, fileInfo: FileInfo): Result { + return runCatching { + innerRoom.sendFile(file.path, fileInfo.map()) + } + } + } 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 28d6525739..a790812137 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 @@ -20,6 +20,10 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId +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.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.timeline.MatrixTimeline @@ -30,6 +34,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow +import java.io.File class FakeMatrixRoom( override val sessionId: SessionId = A_SESSION_ID, @@ -54,6 +59,9 @@ class FakeMatrixRoom( private var updateMembersResult: Result = Result.success(Unit) private var acceptInviteResult = Result.success(Unit) private var rejectInviteResult = Result.success(Unit) + private var sendMediaResult = Result.success(Unit) + var sendMediaCount = 0 + private set var isInviteAccepted: Boolean = false private set @@ -128,6 +136,14 @@ class FakeMatrixRoom( return rejectInviteResult } + override suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo): Result = sendMediaResult.also { sendMediaCount++ } + + override suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo): Result = sendMediaResult.also { sendMediaCount++ } + + override suspend fun sendAudio(file: File, audioInfo: AudioInfo): Result = sendMediaResult.also { sendMediaCount++ } + + override suspend fun sendFile(file: File, fileInfo: FileInfo): Result = sendMediaResult.also { sendMediaCount++ } + override fun close() = Unit fun givenLeaveRoomError(throwable: Throwable?) { @@ -165,4 +181,8 @@ class FakeMatrixRoom( fun givenUnIgnoreResult(result: Result) { unignoreResult = result } + + fun givenSendMediaResult(result: Result) { + sendMediaResult = result + } } diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaUploadInfo.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaUploadInfo.kt index 36eb8f5102..6696806e24 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaUploadInfo.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaUploadInfo.kt @@ -24,8 +24,8 @@ import io.element.android.libraries.matrix.api.media.VideoInfo import java.io.File sealed interface MediaUploadInfo { - data class Image(val file: File, val info: ImageInfo, val thumbnailInfo: ThumbnailProcessingInfo?) : MediaUploadInfo - data class Video(val file: File, val info: VideoInfo, val thumbnailInfo: ThumbnailProcessingInfo?) : MediaUploadInfo + data class Image(val file: File, val info: ImageInfo, val thumbnailInfo: ThumbnailProcessingInfo) : MediaUploadInfo + data class Video(val file: File, val info: VideoInfo, val thumbnailInfo: ThumbnailProcessingInfo) : MediaUploadInfo data class Audio(val file: File, val info: AudioInfo) : MediaUploadInfo data class AnyFile(val file: File, val info: FileInfo) : MediaUploadInfo } @@ -33,4 +33,5 @@ sealed interface MediaUploadInfo { data class ThumbnailProcessingInfo( val file: File, val info: ThumbnailInfo, + val blurhash: String, ) diff --git a/libraries/mediaupload/impl/build.gradle.kts b/libraries/mediaupload/impl/build.gradle.kts index c451d81c7e..a23ab14b74 100644 --- a/libraries/mediaupload/impl/build.gradle.kts +++ b/libraries/mediaupload/impl/build.gradle.kts @@ -41,6 +41,7 @@ android { implementation(libs.androidx.exifinterface) implementation(libs.coroutines.core) implementation(libs.otaliastudios.transcoder) + implementation(libs.vanniktech.blurhash) testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt index 45c09a2a47..96d5e2ea63 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt @@ -19,6 +19,7 @@ package io.element.android.libraries.mediaupload import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory +import com.vanniktech.blurhash.BlurHash import io.element.android.libraries.androidutils.bitmap.calculateInSampleSize import io.element.android.libraries.androidutils.bitmap.resizeToMax import io.element.android.libraries.androidutils.bitmap.rotateToMetadataOrientation @@ -48,14 +49,21 @@ class ImageCompressor @Inject constructor( ): Result = withContext(Dispatchers.IO) { runCatching { val compressedBitmap = compressToBitmap(inputStream, resizeMode).getOrThrow() + val blurhash = BlurHash.encode(compressedBitmap, 3, 3) // Encode bitmap to the destination temporary file - val tmpFile = context.createTmpFile() + val tmpFile = context.createTmpFile(extension = "jpeg") tmpFile.outputStream().use { compressedBitmap.compress(format, desiredQuality, it) } - ImageCompressionResult(tmpFile, compressedBitmap.width, compressedBitmap.height, tmpFile.length()) + ImageCompressionResult( + file = tmpFile, + width = compressedBitmap.width, + height = compressedBitmap.height, + size = tmpFile.length(), + blurhash = blurhash + ) } } @@ -108,6 +116,7 @@ data class ImageCompressionResult( val width: Int, val height: Int, val size: Long, + val blurhash: String, ) sealed interface ResizeMode { diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/MediaPreProcessorImpl.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/MediaPreProcessorImpl.kt index 3d51cb24f1..c6dd7eb841 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/MediaPreProcessorImpl.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/MediaPreProcessorImpl.kt @@ -26,9 +26,7 @@ import io.element.android.libraries.androidutils.file.createTmpFile import io.element.android.libraries.androidutils.media.runAndRelease import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.mimetype.MimeTypes -import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAudio import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage -import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.matrix.api.media.AudioInfo @@ -41,7 +39,6 @@ import io.element.android.libraries.mediaupload.api.MediaType import io.element.android.libraries.mediaupload.api.MediaUploadInfo import io.element.android.libraries.mediaupload.api.ThumbnailProcessingInfo import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.onEach @@ -93,19 +90,23 @@ class MediaPreProcessorImpl @Inject constructor( deleteOriginal: Boolean, ): Result = runCatching { // Camera returns an 'octet-stream' mimetype, so it needs to be overridden - val originalMimeType = contentResolver.getType(uri) - val mimeType = when (mediaType) { - MediaType.Image -> MimeTypes.Images - MediaType.Video -> MimeTypes.Videos - MediaType.Audio -> MimeTypes.Audio - else -> originalMimeType + val mimeType = contentResolver.getType(uri) + val mimeTypeOrDefault = if (mimeType == MimeTypes.OctetStream) { + when(mediaType) { + MediaType.Image -> MimeTypes.Jpeg + MediaType.Video -> MimeTypes.Mp4 + MediaType.Audio -> MimeTypes.Ogg + else -> mimeType + } + } else { + mimeType } val compressBeforeSending = mediaType in sequenceOf(MediaType.Image, MediaType.Video) val result = if (compressBeforeSending && mimeType != MimeTypes.Gif) { - when { - mimeType.isMimeTypeImage() -> processImage(uri) - mimeType.isMimeTypeVideo() -> processVideo(uri, mimeType) - mimeType.isMimeTypeAudio() -> processAudio(uri) + when(mediaType) { + MediaType.Image -> processImage(uri) + MediaType.Video -> processVideo(uri, mimeTypeOrDefault) + MediaType.Audio -> processAudio(uri, mimeTypeOrDefault) else -> error("Cannot compress file of type: $mimeType") } } else { @@ -115,7 +116,7 @@ class MediaPreProcessorImpl @Inject constructor( removeSensitiveImageMetadata(file) } val info = FileInfo( - mimetype = originalMimeType, + mimetype = mimeType, size = file.length(), thumbnailInfo = null, thumbnailUrl = null, @@ -141,7 +142,7 @@ class MediaPreProcessorImpl @Inject constructor( removeSensitiveImageMetadata(compressedFileResult.file) val thumbnailResult = compressedFileResult.file.inputStream().use { generateImageThumbnail(it) } - val processingResult = compressedFileResult.toImageInfo(MimeTypes.Jpeg, thumbnailResult?.file?.path, thumbnailResult?.info) + val processingResult = compressedFileResult.toImageInfo(MimeTypes.Jpeg, thumbnailResult.file.path, thumbnailResult.info) return MediaUploadInfo.Image(compressedFileResult.file, processingResult, thumbnailResult) } @@ -155,32 +156,33 @@ class MediaPreProcessorImpl @Inject constructor( .first() .file - val videoProcessingInfo = extractVideoMetadata(resultFile, mimeType, thumbnailInfo?.file?.path, thumbnailInfo?.info) + val videoProcessingInfo = extractVideoMetadata(resultFile, mimeType, thumbnailInfo.file.path, thumbnailInfo) return MediaUploadInfo.Video(resultFile, videoProcessingInfo, thumbnailInfo) } - private suspend fun processAudio(uri: Uri): MediaUploadInfo { + private suspend fun processAudio(uri: Uri, mimeType: String?): MediaUploadInfo { val file = copyToTmpFile(uri) return MediaMetadataRetriever().runAndRelease { setDataSource(context, Uri.fromFile(file)) val info = AudioInfo( duration = extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L, - size = file.length() + size = file.length(), + mimeType = mimeType, ) MediaUploadInfo.Audio(file, info) } } - private suspend fun generateImageThumbnail(inputStream: InputStream): ThumbnailProcessingInfo? { + private suspend fun generateImageThumbnail(inputStream: InputStream): ThumbnailProcessingInfo { val thumbnailResult = imageCompressor .compressToTmpFile( inputStream = inputStream, resizeMode = ResizeMode.Strict(THUMB_MAX_WIDTH, THUMB_MAX_HEIGHT), - ).getOrNull() + ).getOrThrow() - return thumbnailResult?.toThumbnailProcessingInfo(MimeTypes.Jpeg) + return thumbnailResult.toThumbnailProcessingInfo(MimeTypes.Jpeg) } private fun removeSensitiveImageMetadata(file: File) { @@ -203,7 +205,7 @@ class MediaPreProcessorImpl @Inject constructor( } } - private fun extractVideoMetadata(file: File, mimeType: String?, thumbnailUrl: String?, thumbnailInfo: ThumbnailInfo?): VideoInfo = + private fun extractVideoMetadata(file: File, mimeType: String?, thumbnailUrl: String?, thumbnailInfo: ThumbnailProcessingInfo?): VideoInfo = MediaMetadataRetriever().runAndRelease { setDataSource(context, Uri.fromFile(file)) @@ -213,16 +215,16 @@ class MediaPreProcessorImpl @Inject constructor( height = extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)?.toLong() ?: 0L, mimetype = mimeType, size = file.length(), - thumbnailInfo = thumbnailInfo, + thumbnailInfo = thumbnailInfo?.info, thumbnailUrl = thumbnailUrl, - blurhash = null, + blurhash = thumbnailInfo?.blurhash, ) } - private suspend fun extractVideoThumbnail(uri: Uri): ThumbnailProcessingInfo? = + private suspend fun extractVideoThumbnail(uri: Uri): ThumbnailProcessingInfo = MediaMetadataRetriever().runAndRelease { setDataSource(context, uri) - val bitmap = getFrameAtTime(VIDEO_THUMB_FRAME) ?: return@runAndRelease null + val bitmap = requireNotNull(getFrameAtTime(VIDEO_THUMB_FRAME)) val inputStream = ByteArrayOutputStream().use { bitmap.compress(Bitmap.CompressFormat.JPEG, 80, it) ByteArrayInputStream(it.toByteArray()) @@ -249,7 +251,7 @@ fun ImageCompressionResult.toImageInfo(mimeType: String, thumbnailUrl: String?, size = size, thumbnailInfo = thumbnailInfo, thumbnailUrl = thumbnailUrl, - blurhash = null, + blurhash = blurhash, ) fun ImageCompressionResult.toThumbnailProcessingInfo(mimeType: String) = ThumbnailProcessingInfo( @@ -260,4 +262,5 @@ fun ImageCompressionResult.toThumbnailProcessingInfo(mimeType: String) = Thumbna mimetype = mimeType, size = size, ), + blurhash = blurhash, ) diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/VideoCompressor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/VideoCompressor.kt index ea0dbf28fd..490e286353 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/VideoCompressor.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/VideoCompressor.kt @@ -32,7 +32,7 @@ class VideoCompressor @Inject constructor( ) { fun compress(uri: Uri) = callbackFlow { - val tmpFile = context.createTmpFile() + val tmpFile = context.createTmpFile(extension = "mp4") val future = Transcoder.into(tmpFile.path) .addDataSource(context, uri) .setListener(object : TranscoderListener { From f33742f609ba9b98e7f5ec944a1de2f4d08e91c6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 May 2023 16:01:30 +0000 Subject: [PATCH 03/18] Update kotlin to v1.7.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 3504a3b0fe..40d89b7dd2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,7 +23,7 @@ compose_bom = "2023.04.01" composecompiler = "1.4.7" # Coroutines -coroutines = "1.6.4" +coroutines = "1.7.0" # Accompanist accompanist = "0.30.1" From 719dd20555f998773d54d6e0b269145aca336124 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Fri, 12 May 2023 10:05:39 +0200 Subject: [PATCH 04/18] Remove useless OptIn in tests --- .../android/appnav/RootPresenterTest.kt | 3 - .../appnav/loggedin/LoggedInPresenterTest.kt | 3 - .../impl/AllMatrixUsersDataSourceTest.kt | 2 - .../impl/addpeople/AddPeoplePresenterTests.kt | 3 - .../ConfigureRoomPresenterTests.kt | 3 - .../impl/root/CreateRoomRootPresenterTests.kt | 3 - .../impl/InviteListPresenterTests.kt | 3 - .../changeserver/ChangeServerPresenterTest.kt | 4 - .../login/impl/root/LoginRootPresenterTest.kt | 7 -- .../impl/LogoutPreferencePresenterTest.kt | 3 - .../messages/MessagesPresenterTest.kt | 3 - .../actionlist/ActionListPresenterTest.kt | 5 +- .../messages/fixtures/timelineItemsFactory.kt | 3 - .../MessageComposerPresenterTest.kt | 79 +++++++++---------- .../timeline/TimelinePresenterTest.kt | 4 - .../DeveloperSettingsPresenterTest.kt | 3 - .../impl/root/PreferencesRootPresenterTest.kt | 3 - .../impl/bugreport/BugReportPresenterTest.kt | 3 - .../crash/ui/CrashDetectionPresenterTest.kt | 3 - .../RageshakeDetectionPresenterTest.kt | 3 - .../RageshakePreferencesPresenterTest.kt | 3 - .../impl/DefaultInviteStateDataSourceTest.kt | 3 - .../roomlist/impl/RoomListPresenterTests.kt | 3 +- .../impl/DefaultUserListPresenterTests.kt | 2 - .../impl/DefaultPermissionsPresenterTest.kt | 3 +- .../noop/NoopPermissionsPresenterTest.kt | 3 - .../clientsecret/PushClientSecretImplTest.kt | 3 - .../impl/DatabaseSessionStoreTests.kt | 2 - .../DefaultAppNavigationStateServiceTest.kt | 4 +- 29 files changed, 42 insertions(+), 127 deletions(-) diff --git a/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt index 2938ce41df..7c45620afd 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.appnav import app.cash.molecule.RecompositionClock @@ -30,7 +28,6 @@ import io.element.android.features.rageshake.test.crash.FakeCrashDataStore import io.element.android.features.rageshake.test.rageshake.FakeRageShake import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataStore import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt index 71d303150b..2fd6d2d8f7 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.appnav.loggedin import app.cash.molecule.RecompositionClock @@ -29,7 +27,6 @@ import io.element.android.libraries.permissions.noop.NoopPermissionsPresenter import io.element.android.libraries.push.api.PushService import io.element.android.libraries.push.providers.api.Distributor import io.element.android.libraries.push.providers.api.PushProvider -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/AllMatrixUsersDataSourceTest.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/AllMatrixUsersDataSourceTest.kt index f20e46d1ba..043ad06361 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/AllMatrixUsersDataSourceTest.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/AllMatrixUsersDataSourceTest.kt @@ -25,11 +25,9 @@ import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID_2 import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.FakeMatrixClient -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test -@OptIn(ExperimentalCoroutinesApi::class) internal class AllMatrixUsersDataSourceTest { @Test diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt index 7ca2f0147f..d7ee836453 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.createroom.impl.addpeople import app.cash.molecule.RecompositionClock @@ -26,7 +24,6 @@ import io.element.android.features.createroom.impl.CreateRoomDataStore import io.element.android.features.userlist.api.UserListDataStore import io.element.android.features.userlist.test.FakeUserListDataSource import io.element.android.features.userlist.test.FakeUserListPresenterFactory -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt index 8be1e777a8..24076bcd55 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.createroom.impl.configureroom import android.net.Uri @@ -36,7 +34,6 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.ui.components.aMatrixUser import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt index 238fa8f58d..2a06278934 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.createroom.impl.root import app.cash.molecule.RecompositionClock @@ -35,7 +33,6 @@ import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import kotlinx.collections.immutable.persistentListOf -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test diff --git a/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt b/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt index b8a99b40cc..462faa405f 100644 --- a/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt +++ b/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt @@ -38,11 +38,9 @@ import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test -@OptIn(ExperimentalCoroutinesApi::class) class InviteListPresenterTests { @Test @@ -509,5 +507,4 @@ class InviteListPresenterTests { unreadNotificationCount = 0, ) ) - } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt index c961b7fce7..cc216cc9dd 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.login.impl.changeserver import app.cash.molecule.RecompositionClock @@ -28,11 +26,9 @@ import io.element.android.libraries.matrix.test.A_HOMESERVER_URL import io.element.android.libraries.matrix.test.A_HOMESERVER_URL_2 import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test -@OptIn(ExperimentalCoroutinesApi::class) class ChangeServerPresenterTest { @Test fun `present - should start with default homeserver`() = runTest { diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/root/LoginRootPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/root/LoginRootPresenterTest.kt index 6c825d0edd..be940feacf 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/root/LoginRootPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/root/LoginRootPresenterTest.kt @@ -14,25 +14,18 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.login.impl.root import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.features.login.impl.root.LoggedInState -import io.element.android.features.login.impl.root.LoginFormState -import io.element.android.features.login.impl.root.LoginRootEvents -import io.element.android.features.login.impl.root.LoginRootPresenter import io.element.android.libraries.matrix.test.A_HOMESERVER import io.element.android.libraries.matrix.test.A_PASSWORD import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt index 6429ff1938..7a3556389f 100644 --- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.logout.impl import app.cash.molecule.RecompositionClock @@ -28,7 +26,6 @@ import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.FakeMatrixClient -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index 6756f55f9c..e6ee61ee52 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.messages import app.cash.molecule.RecompositionClock @@ -40,7 +38,6 @@ import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.mediapickers.test.FakePickerProvider import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor import io.element.android.libraries.textcomposer.MessageComposerMode -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt index 410f7186aa..cd85418bff 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.messages.actionlist import app.cash.molecule.RecompositionClock @@ -23,9 +21,9 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.actionlist.ActionListEvents -import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.impl.actionlist.ActionListPresenter import io.element.android.features.messages.impl.actionlist.ActionListState +import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent @@ -37,7 +35,6 @@ import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_NAME import kotlinx.collections.immutable.persistentListOf -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt index 2d4ee3842c..058406d513 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.messages.fixtures import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory @@ -34,7 +32,6 @@ import io.element.android.features.messages.impl.timeline.factories.virtual.Time import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory import io.element.android.libraries.dateformatter.test.FakeDaySeparatorFormatter import io.element.android.tests.testutils.testCoroutineDispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi internal fun aTimelineItemsFactory() = TimelineItemsFactory( dispatchers = testCoroutineDispatchers(), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt index 2ef7aa0c69..9639759529 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.messages.textcomposer import app.cash.molecule.RecompositionClock @@ -52,14 +50,9 @@ import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor import io.element.android.libraries.textcomposer.MessageComposerMode import io.mockk.mockk import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.android.awaitFrame -import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest -import kotlinx.coroutines.test.setMain import org.junit.Test import java.io.File @@ -302,30 +295,32 @@ class MessageComposerPresenterTest { val room = FakeMatrixRoom() val presenter = createPresenter(this, room = room) pickerProvider.givenMimeType(MimeTypes.Images) - mediaPreProcessor.givenResult(Result.success( - MediaUploadInfo.Image( - file = File("/some/path"), - info = ImageInfo( - width = null, - height = null, - mimetype = null, - size = null, - thumbnailInfo = null, - thumbnailUrl = null, - blurhash = null, - ), - thumbnailInfo = ThumbnailProcessingInfo( + mediaPreProcessor.givenResult( + Result.success( + MediaUploadInfo.Image( file = File("/some/path"), - info = ThumbnailInfo( + info = ImageInfo( width = null, height = null, mimetype = null, size = null, + thumbnailInfo = null, + thumbnailUrl = null, + blurhash = null, ), - blurhash = "", + thumbnailInfo = ThumbnailProcessingInfo( + file = File("/some/path"), + info = ThumbnailInfo( + width = null, + height = null, + mimetype = null, + size = null, + ), + blurhash = "", + ) ) ) - )) + ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -342,31 +337,33 @@ class MessageComposerPresenterTest { val room = FakeMatrixRoom() val presenter = createPresenter(this, room = room) pickerProvider.givenMimeType(MimeTypes.Videos) - mediaPreProcessor.givenResult(Result.success( - MediaUploadInfo.Video( - file = File("/some/path"), - info = VideoInfo( - width = null, - height = null, - mimetype = null, - duration = null, - size = null, - thumbnailInfo = null, - thumbnailUrl = null, - blurhash = null, - ), - thumbnailInfo = ThumbnailProcessingInfo( + mediaPreProcessor.givenResult( + Result.success( + MediaUploadInfo.Video( file = File("/some/path"), - info = ThumbnailInfo( + info = VideoInfo( width = null, height = null, mimetype = null, + duration = null, size = null, + thumbnailInfo = null, + thumbnailUrl = null, + blurhash = null, ), - blurhash = "", + thumbnailInfo = ThumbnailProcessingInfo( + file = File("/some/path"), + info = ThumbnailInfo( + width = null, + height = null, + mimetype = null, + size = null, + ), + blurhash = "", + ) ) ) - )) + ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -394,7 +391,7 @@ class MessageComposerPresenterTest { @Test fun `present - Pick media from gallery & cancel does nothing`() = runTest { val presenter = createPresenter(this) - with(pickerProvider){ + with(pickerProvider) { givenResult(null) // Simulate a user canceling the flow givenMimeType(MimeTypes.Images) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt index 41f37158d9..a88b2251a4 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.messages.timeline import app.cash.molecule.RecompositionClock @@ -27,8 +25,6 @@ import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.TimelinePresenter import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.room.FakeMatrixRoom -import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt index 2e4bd005ca..6b7c8c2df4 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.preferences.impl.developer import app.cash.molecule.RecompositionClock @@ -24,7 +22,6 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt index 38e97148ca..791b0cf533 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.preferences.impl.root import app.cash.molecule.RecompositionClock @@ -29,7 +27,6 @@ import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataSto import io.element.android.libraries.architecture.Async import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.matrix.test.FakeMatrixClient -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt index 1390dd1c8c..3df6c62287 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.rageshake.impl.bugreport import app.cash.molecule.RecompositionClock @@ -28,7 +26,6 @@ import io.element.android.features.rageshake.test.screenshot.A_SCREENSHOT_URI import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.test.A_FAILURE_REASON -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt index 3a47ab7797..2d9834607f 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.rageshake.impl.crash.ui import app.cash.molecule.RecompositionClock @@ -26,7 +24,6 @@ import io.element.android.features.rageshake.api.crash.CrashDetectionEvents import io.element.android.features.rageshake.impl.crash.DefaultCrashDetectionPresenter import io.element.android.features.rageshake.test.crash.A_CRASH_DATA import io.element.android.features.rageshake.test.crash.FakeCrashDataStore -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt index cca82d64d5..88135d5633 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.rageshake.impl.detection import android.graphics.Bitmap @@ -31,7 +29,6 @@ import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataSto import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.mockk.mockk -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/preferences/RageshakePreferencesPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/preferences/RageshakePreferencesPresenterTest.kt index e10e485d79..b01ce22645 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/preferences/RageshakePreferencesPresenterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/preferences/RageshakePreferencesPresenterTest.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.rageshake.impl.preferences import app.cash.molecule.RecompositionClock @@ -26,7 +24,6 @@ import io.element.android.features.rageshake.api.preferences.RageshakePreference import io.element.android.features.rageshake.test.rageshake.A_SENSITIVITY import io.element.android.features.rageshake.test.rageshake.FakeRageShake import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataStore -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/DefaultInviteStateDataSourceTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/DefaultInviteStateDataSourceTest.kt index 28b8219189..040a5b5b50 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/DefaultInviteStateDataSourceTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/DefaultInviteStateDataSourceTest.kt @@ -27,11 +27,9 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled import io.element.android.tests.testutils.testCoroutineDispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test -@OptIn(ExperimentalCoroutinesApi::class) internal class DefaultInviteStateDataSourceTest { @Test @@ -133,5 +131,4 @@ internal class DefaultInviteStateDataSourceTest { Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NoInvites) } } - } diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index 9030b818bc..104c419f74 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -38,12 +38,11 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.Test -@OptIn(ExperimentalCoroutinesApi::class) class RoomListPresenterTests { +class RoomListPresenterTests { @Test fun `present - should start with no user and then load user with success`() = runTest { diff --git a/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt b/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt index cf9d9da642..ae2f709608 100644 --- a/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt +++ b/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt @@ -33,11 +33,9 @@ import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.mockk.coJustRun import io.mockk.mockkConstructor import kotlinx.collections.immutable.persistentListOf -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test -@OptIn(ExperimentalCoroutinesApi::class) class DefaultUserListPresenterTests { private val userListDataSource = FakeUserListDataSource() diff --git a/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt index 10e6edf3f5..24b174426c 100644 --- a/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt +++ b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalPermissionsApi::class) +@file:OptIn(ExperimentalPermissionsApi::class) package io.element.android.libraries.permissions.impl @@ -25,7 +25,6 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.PermissionStatus import com.google.common.truth.Truth.assertThat import io.element.android.libraries.permissions.api.PermissionsEvents -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/libraries/permissions/noop/src/test/kotlin/io/element/android/libraries/permissions/noop/NoopPermissionsPresenterTest.kt b/libraries/permissions/noop/src/test/kotlin/io/element/android/libraries/permissions/noop/NoopPermissionsPresenterTest.kt index 36b908cb0d..9992480436 100644 --- a/libraries/permissions/noop/src/test/kotlin/io/element/android/libraries/permissions/noop/NoopPermissionsPresenterTest.kt +++ b/libraries/permissions/noop/src/test/kotlin/io/element/android/libraries/permissions/noop/NoopPermissionsPresenterTest.kt @@ -14,15 +14,12 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.libraries.permissions.noop import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretImplTest.kt b/libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretImplTest.kt index 4f5316497d..48e4daa40c 100644 --- a/libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretImplTest.kt +++ b/libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretImplTest.kt @@ -14,13 +14,10 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.libraries.pushstore.impl.clientsecret import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.core.SessionId -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt index 0260604f6e..28b9dfba50 100644 --- a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt @@ -20,12 +20,10 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver import io.element.android.libraries.matrix.session.SessionData -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test -@OptIn(ExperimentalCoroutinesApi::class) class DatabaseSessionStoreTests { private lateinit var database: SessionDatabase diff --git a/services/appnavstate/impl/src/test/kotlin/io/element/android/services/appnavstate/impl/DefaultAppNavigationStateServiceTest.kt b/services/appnavstate/impl/src/test/kotlin/io/element/android/services/appnavstate/impl/DefaultAppNavigationStateServiceTest.kt index 1ccd4c5b7a..d6000dc1d8 100644 --- a/services/appnavstate/impl/src/test/kotlin/io/element/android/services/appnavstate/impl/DefaultAppNavigationStateServiceTest.kt +++ b/services/appnavstate/impl/src/test/kotlin/io/element/android/services/appnavstate/impl/DefaultAppNavigationStateServiceTest.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.services.appnavstate.impl import com.google.common.truth.Truth.assertThat @@ -28,11 +26,11 @@ import io.element.android.services.appnavstate.test.A_ROOM_OWNER import io.element.android.services.appnavstate.test.A_SESSION_OWNER import io.element.android.services.appnavstate.test.A_SPACE_OWNER import io.element.android.services.appnavstate.test.A_THREAD_OWNER -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import org.junit.Assert.assertThrows import org.junit.Test + class DefaultAppNavigationStateServiceTest { @Test From 92e9d3a1279e2f50b79f9ee7b343eb1fc834ada0 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 12 May 2023 11:50:39 +0100 Subject: [PATCH 05/18] Fix a few FFI leaks (#405) Fix a few FFI leaks These are instances where we obtain an FFIObject and don't call Close on it to release the underlying reference on the Rust side. The worst instance here was leaking an object per room member every time we refreshed the member list --- .../assertions/assertRoomListSynced.yaml | 5 +++ .maestro/tests/roomList/searchRoomList.yaml | 1 + .../impl/root/CreateRoomRootPresenter.kt | 11 ++++--- .../invitelist/impl/InviteListPresenter.kt | 8 +++-- .../matrix/impl/room/RoomMemberMapper.kt | 19 ++++++----- .../matrix/impl/room/RustMatrixRoom.kt | 7 ++-- .../impl/timeline/MatrixTimelineItemMapper.kt | 4 +-- .../timeline/item/event/EventMessageMapper.kt | 8 ++--- .../item/event/EventTimelineItemMapper.kt | 24 +++++++------- .../item/event/TimelineEventContentMapper.kt | 6 ++-- .../android/samples/minimal/RoomListScreen.kt | 33 ++++++++----------- 11 files changed, 67 insertions(+), 59 deletions(-) create mode 100644 .maestro/tests/assertions/assertRoomListSynced.yaml diff --git a/.maestro/tests/assertions/assertRoomListSynced.yaml b/.maestro/tests/assertions/assertRoomListSynced.yaml new file mode 100644 index 0000000000..2d13c17df9 --- /dev/null +++ b/.maestro/tests/assertions/assertRoomListSynced.yaml @@ -0,0 +1,5 @@ +appId: ${APP_ID} +--- +- extendedWaitUntil: + visible: ${ROOM_NAME} + timeout: 10_000 diff --git a/.maestro/tests/roomList/searchRoomList.yaml b/.maestro/tests/roomList/searchRoomList.yaml index fe859defbb..45138eb9aa 100644 --- a/.maestro/tests/roomList/searchRoomList.yaml +++ b/.maestro/tests/roomList/searchRoomList.yaml @@ -1,5 +1,6 @@ appId: ${APP_ID} --- +- runFlow: ../assertions/assertRoomListSynced.yaml - tapOn: "search" - inputText: ${ROOM_NAME.substring(0, 3)} - takeScreenshot: build/maestro/400-SearchRoom diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt index 60056872db..0ac9611fc7 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt @@ -65,11 +65,12 @@ class CreateRoomRootPresenter @Inject constructor( fun startDm(matrixUser: MatrixUser) { startDmAction.value = Async.Uninitialized - val existingDM = matrixClient.findDM(matrixUser.userId) - if (existingDM == null) { - localCoroutineScope.createDM(matrixUser, startDmAction) - } else { - startDmAction.value = Async.Success(existingDM.roomId) + matrixClient.findDM(matrixUser.userId).use { existingDM -> + if (existingDM == null) { + localCoroutineScope.createDM(matrixUser, startDmAction) + } else { + startDmAction.value = Async.Success(existingDM.roomId) + } } } diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt index dc9302b908..db9fec3153 100644 --- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt +++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt @@ -131,14 +131,18 @@ class InviteListPresenter @Inject constructor( private fun CoroutineScope.acceptInvite(roomId: RoomId, acceptedAction: MutableState>) = launch { suspend { - client.getRoom(roomId)?.acceptInvitation()?.getOrThrow() + client.getRoom(roomId)?.use { + it.acceptInvitation().getOrThrow() + } roomId }.execute(acceptedAction) } private fun CoroutineScope.declineInvite(roomId: RoomId, declinedAction: MutableState>) = launch { suspend { - client.getRoom(roomId)?.rejectInvitation()?.getOrThrow() ?: Unit + client.getRoom(roomId)?.use { + it.rejectInvitation().getOrThrow() + } ?: Unit }.execute(declinedAction) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomMemberMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomMemberMapper.kt index 2d2be4f36a..e79a8088aa 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomMemberMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomMemberMapper.kt @@ -24,17 +24,18 @@ import org.matrix.rustcomponents.sdk.RoomMember as RustRoomMember object RoomMemberMapper { - fun map(roomMember: RustRoomMember): RoomMember = + fun map(roomMember: RustRoomMember): RoomMember = roomMember.use { RoomMember( - UserId(roomMember.userId()), - roomMember.displayName(), - roomMember.avatarUrl(), - mapMembership(roomMember.membership()), - roomMember.isNameAmbiguous(), - roomMember.powerLevel(), - roomMember.normalizedPowerLevel(), - roomMember.isIgnored(), + UserId(it.userId()), + it.displayName(), + it.avatarUrl(), + mapMembership(it.membership()), + it.isNameAmbiguous(), + it.powerLevel(), + it.normalizedPowerLevel(), + it.isIgnored(), ) + } fun mapMembership(membershipState: RustMembershipState): RoomMembershipState = when (membershipState) { 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 3930ebebe6..ae35449a8e 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 @@ -161,9 +161,10 @@ class RustMatrixRoom( override suspend fun sendMessage(message: String): Result = withContext(coroutineDispatchers.io) { val transactionId = genTransactionId() - val content = messageEventContentFromMarkdown(message) - runCatching { - innerRoom.send(content, transactionId) + messageEventContentFromMarkdown(message).use { content -> + runCatching { + innerRoom.send(content, transactionId) + } } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItemMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItemMapper.kt index 7151f3550b..5b4eaa9e36 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItemMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineItemMapper.kt @@ -27,12 +27,12 @@ class MatrixTimelineItemMapper( ) { fun map(timelineItem: TimelineItem): MatrixTimelineItem = timelineItem.use { - val asEvent = timelineItem.asEvent() + val asEvent = it.asEvent() if (asEvent != null) { val eventTimelineItem = eventTimelineItemMapper.map(asEvent) return MatrixTimelineItem.Event(eventTimelineItem) } - val asVirtual = timelineItem.asVirtual() + val asVirtual = it.asVirtual() if (asVirtual != null) { val virtualTimelineItem = virtualTimelineItemMapper.map(asVirtual) return MatrixTimelineItem.Virtual(virtualTimelineItem) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt index a000783e86..674e02a13d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt @@ -39,7 +39,7 @@ import org.matrix.rustcomponents.sdk.MessageFormat as RustMessageFormat class EventMessageMapper { fun map(message: Message): MessageContent = message.use { - val type = message.msgtype().use { type -> + val type = it.msgtype().use { type -> when (type) { is MessageType.Audio -> { AudioMessageType(type.content.body, type.content.source.useUrl(), type.content.info?.map()) @@ -68,9 +68,9 @@ class EventMessageMapper { } } MessageContent( - body = message.body(), - inReplyTo = message.inReplyTo()?.eventId?.let(::EventId), - isEdited = message.isEdited(), + body = it.body(), + inReplyTo = it.inReplyTo()?.eventId?.let(::EventId), + isEdited = it.isEdited(), type = type ) } 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 3ec412ae7d..726b448099 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 @@ -31,18 +31,18 @@ class EventTimelineItemMapper(private val contentMapper: TimelineEventContentMap fun map(eventTimelineItem: RustEventTimelineItem): EventTimelineItem = eventTimelineItem.use { EventTimelineItem( - uniqueIdentifier = eventTimelineItem.uniqueIdentifier(), - eventId = eventTimelineItem.eventId()?.let { EventId(it) }, - isEditable = eventTimelineItem.isEditable(), - isLocal = eventTimelineItem.isLocal(), - isOwn = eventTimelineItem.isOwn(), - isRemote = eventTimelineItem.isRemote(), - localSendState = eventTimelineItem.localSendState()?.map(), - reactions = eventTimelineItem.reactions().map(), - sender = UserId(eventTimelineItem.sender()), - senderProfile = eventTimelineItem.senderProfile().map(), - timestamp = eventTimelineItem.timestamp().toLong(), - content = contentMapper.map(eventTimelineItem.content()) + uniqueIdentifier = it.uniqueIdentifier(), + eventId = it.eventId()?.let { EventId(it) }, + isEditable = it.isEditable(), + isLocal = it.isLocal(), + isOwn = it.isOwn(), + isRemote = it.isRemote(), + localSendState = it.localSendState()?.map(), + reactions = it.reactions().map(), + sender = UserId(it.sender()), + senderProfile = it.senderProfile().map(), + timestamp = it.timestamp().toLong(), + content = contentMapper.map(it.content()) ) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt index 51e84b441f..f776c52670 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt @@ -17,6 +17,7 @@ package io.element.android.libraries.matrix.impl.timeline.item.event import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.timeline.item.event.EventContent import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange @@ -26,7 +27,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.RedactedConte import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent import io.element.android.libraries.matrix.api.timeline.item.event.StateContent import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent -import io.element.android.libraries.matrix.api.timeline.item.event.EventContent import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent import io.element.android.libraries.matrix.impl.media.map @@ -39,7 +39,7 @@ import org.matrix.rustcomponents.sdk.OtherState as RustOtherState class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMapper = EventMessageMapper()) { fun map(content: TimelineItemContent): EventContent = content.use { - when (val kind = content.kind()) { + when (val kind = it.kind()) { is TimelineItemContentKind.FailedToParseMessageLike -> { FailedToParseMessageLikeContent( eventType = kind.eventType, @@ -54,7 +54,7 @@ class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMap ) } TimelineItemContentKind.Message -> { - val message = content.asMessage() + val message = it.asMessage() if (message == null) { UnknownContent } else { diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt index df1f052e79..aab7d5ca5f 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt @@ -33,17 +33,16 @@ import io.element.android.libraries.dateformatter.impl.LocalDateTimeProvider import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import kotlinx.datetime.Clock import kotlinx.datetime.TimeZone import java.util.Locale -import java.util.concurrent.Executors class RoomListScreen( context: Context, private val matrixClient: MatrixClient, + private val coroutineDispatchers: CoroutineDispatchers = Singleton.coroutineDispatchers, ) { private val clock = Clock.System private val locale = Locale.getDefault() @@ -58,28 +57,24 @@ class RoomListScreen( sessionVerificationService = sessionVerificationService, networkMonitor = NetworkMonitorImpl(context), snackbarDispatcher = SnackbarDispatcher(), - inviteStateDataSource = DefaultInviteStateDataSource( - matrixClient, - DefaultSeenInvitesStore(context), - CoroutineDispatchers( - io = Dispatchers.IO, - computation = Dispatchers.Default, - main = Dispatchers.Main, - diffUpdateDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() - ) - ) + inviteStateDataSource = DefaultInviteStateDataSource(matrixClient, DefaultSeenInvitesStore(context), coroutineDispatchers) ) @Composable fun Content(modifier: Modifier = Modifier) { fun onRoomClicked(roomId: RoomId) { - val room = matrixClient.getRoom(roomId)!! - val timeline = room.timeline() Singleton.appScope.launch { - timeline.apply { - initialize() - paginateBackwards(20, 50) - dispose() + withContext(coroutineDispatchers.io) { + matrixClient.getRoom(roomId)!!.use { room -> + val timeline = room.timeline() + + timeline.apply { + // TODO This doesn't work reliably as initialize is asynchronous, and the timeline can't be used until it's finished + initialize() + paginateBackwards(20, 50) + dispose() + } + } } } } From 04930167a399604b40ea4c48449187cb4a3386e4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 May 2023 10:55:58 +0000 Subject: [PATCH 06/18] Update dependency org.matrix.rustcomponents:sdk-android to v0.1.13 --- 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 3504a3b0fe..11131203d8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -129,7 +129,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" } timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.12" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.13" sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" } sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" } From d7d1d0154383f484fff422f9c8ec2cf24bffe7a5 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Fri, 12 May 2023 13:28:22 +0200 Subject: [PATCH 07/18] Add missing OptIn --- .../messages/textcomposer/MessageComposerPresenterTest.kt | 3 +++ .../element/android/libraries/matrix/impl/RustMatrixClient.kt | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt index 9639759529..0c0b46cbe2 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package io.element.android.features.messages.textcomposer import app.cash.molecule.RecompositionClock @@ -50,6 +52,7 @@ import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor import io.element.android.libraries.textcomposer.MessageComposerMode import io.mockk.mockk import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest 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 cce7ac90c7..8709db67f0 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 @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package io.element.android.libraries.matrix.impl import io.element.android.libraries.core.coroutine.CoroutineDispatchers @@ -300,7 +302,6 @@ class RustMatrixClient constructor( } } - @ExperimentalCoroutinesApi override fun close() { slidingSyncUpdateJob?.cancel() stopSync() From 53adb456ba63f77fdd1ee6f4accfd5c6d8dec280 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Fri, 12 May 2023 13:30:06 +0200 Subject: [PATCH 08/18] Test cleanup --- .../userlist/impl/DefaultUserListPresenterTests.kt | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt b/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt index ae2f709608..b13881dc5b 100644 --- a/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt +++ b/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt @@ -16,7 +16,6 @@ package io.element.android.features.userlist.impl -import androidx.compose.foundation.lazy.LazyListState import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test @@ -30,8 +29,6 @@ import io.element.android.features.userlist.test.FakeUserListDataSource import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUser -import io.mockk.coJustRun -import io.mockk.mockkConstructor import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.test.runTest import org.junit.Test @@ -134,9 +131,6 @@ class DefaultUserListPresenterTests { @Test fun `present - select a user`() = runTest { - mockkConstructor(LazyListState::class) - coJustRun { anyConstructed().scrollToItem(index = any()) } - val presenter = DefaultUserListPresenter( UserListPresenterArgs(selectionMode = SelectionMode.Single), userListDataSource, @@ -156,16 +150,15 @@ class DefaultUserListPresenterTests { assertThat(awaitItem().selectedUsers).containsExactly(userA) initialState.eventSink(UserListEvents.AddToSelection(userB)) - // the last added user should be presented first - assertThat(awaitItem().selectedUsers).containsExactly(userB, userA) + assertThat(awaitItem().selectedUsers).containsExactly(userA, userB) initialState.eventSink(UserListEvents.AddToSelection(userABis)) initialState.eventSink(UserListEvents.AddToSelection(userC)) // duplicated users should be ignored - assertThat(awaitItem().selectedUsers).containsExactly(userC, userB, userA) + assertThat(awaitItem().selectedUsers).containsExactly(userA, userB, userC) initialState.eventSink(UserListEvents.RemoveFromSelection(userB)) - assertThat(awaitItem().selectedUsers).containsExactly(userC, userA) + assertThat(awaitItem().selectedUsers).containsExactly(userA, userC) initialState.eventSink(UserListEvents.RemoveFromSelection(userA)) assertThat(awaitItem().selectedUsers).containsExactly(userC) initialState.eventSink(UserListEvents.RemoveFromSelection(userC)) From 394ef825ef799c7db237eda9c68a2cb87d3a9f3d Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Fri, 12 May 2023 15:34:35 +0200 Subject: [PATCH 09/18] increase test timeout --- .../impl/detection/RageshakeDetectionPresenterTest.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt index 88135d5633..369cb0833b 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt @@ -32,6 +32,7 @@ import io.mockk.mockk import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import org.junit.Test +import kotlin.time.Duration.Companion.seconds class RageshakeDetectionPresenterTest { @Test @@ -98,7 +99,7 @@ class RageshakeDetectionPresenterTest { ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() - }.test { + }.test(timeout = 30.seconds) { skipItems(1) val initialState = awaitItem() assertThat(initialState.isStarted).isFalse() @@ -152,7 +153,7 @@ class RageshakeDetectionPresenterTest { } @Test - fun `present - screenshot then disable`() = runTest { + fun `present - screenshot then disable`() = runTest(timeout = 1.seconds) { val screenshotHolder = FakeScreenshotHolder(screenshotUri = null) val rageshake = FakeRageShake(isAvailableValue = true) val rageshakeDataStore = FakeRageshakeDataStore(isEnabled = true) From 32d8bf0b9daf5dceb407d7aafd73b6c5abacadb8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 May 2023 19:15:53 +0000 Subject: [PATCH 10/18] Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-test to v1.7.1 --- 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 40d89b7dd2..4a841a0d64 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,7 +23,7 @@ compose_bom = "2023.04.01" composecompiler = "1.4.7" # Coroutines -coroutines = "1.7.0" +coroutines = "1.7.1" # Accompanist accompanist = "0.30.1" From a336e5cad67b8c80a3d61dccbd5cdc8092d9735b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 May 2023 21:51:50 +0000 Subject: [PATCH 11/18] Update dependency com.google.dagger:dagger-compiler to v2.46.1 --- 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 40d89b7dd2..c4ff7595e1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -43,7 +43,7 @@ stem = "2.3.0" sqldelight = "1.5.5" # DI -dagger = "2.46" +dagger = "2.46.1" anvil = "2.4.5" # quality From 457b83184a046df877be3fd7907514f9f94aab71 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 15 May 2023 12:28:31 +0200 Subject: [PATCH 12/18] Use default material3 version again and cleanup `@OptIn` annotations. Material3 is now 1.1.0 --- .../features/createroom/impl/addpeople/AddPeopleView.kt | 1 - .../createroom/impl/configureroom/ConfigureRoomView.kt | 3 +-- .../features/createroom/impl/root/CreateRoomRootView.kt | 1 - .../element/android/features/messages/impl/MessagesView.kt | 7 ++----- .../features/messages/impl/actionlist/ActionListView.kt | 5 +++-- .../messages/textcomposer/MessageComposerPresenterTest.kt | 3 --- .../android/features/onboarding/impl/OnBoardingScreen.kt | 3 --- .../roomdetails/impl/members/RoomMemberListView.kt | 1 - .../element/android/features/roomlist/impl/RoomListView.kt | 2 +- .../features/roomlist/impl/components/RoomListTopBar.kt | 2 -- gradle/libs.versions.toml | 2 +- .../components/preferences/PreferenceScreen.kt | 1 - .../theme/components/CenterAlignedTopAppBar.kt | 2 -- .../designsystem/theme/components/MediumTopAppBar.kt | 3 +-- .../theme/components/ModalBottomSheetLayout.kt | 3 +-- .../designsystem/theme/components/OutlinedTextField.kt | 2 +- .../libraries/designsystem/theme/components/Scaffold.kt | 2 -- .../libraries/designsystem/theme/components/SearchBar.kt | 3 +-- .../libraries/designsystem/theme/components/TopAppBar.kt | 2 -- .../android/libraries/matrix/impl/RustMatrixClient.kt | 3 --- .../matrix/impl/room/RustRoomSummaryDataSource.kt | 1 - plugins/src/main/kotlin/extension/DependencyHandleScope.kt | 4 +--- .../io/element/android/samples/minimal/MainActivity.kt | 3 --- .../android/tests/testutils/TestCoroutineDispatchers.kt | 3 --- 24 files changed, 13 insertions(+), 49 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt index ebf3e9d508..3746fef4df 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt @@ -41,7 +41,6 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.ui.strings.R as StringR -@OptIn(ExperimentalMaterial3Api::class) @Composable fun AddPeopleView( state: UserListState, 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 cb699f4599..3a59760ac8 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,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalMaterial3Api::class) - package io.element.android.features.createroom.impl.configureroom import android.net.Uri @@ -129,6 +127,7 @@ fun ConfigureRoomView( } } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun ConfigureRoomToolbar( isNextActionEnabled: Boolean, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt index a80cf821fd..2de52b89f7 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt @@ -55,7 +55,6 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.designsystem.R as DrawableR import io.element.android.libraries.ui.strings.R as StringR -@OptIn(ExperimentalMaterial3Api::class) @Composable fun CreateRoomRootView( state: CreateRoomRootState, 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 cd70dc97cd..f98b19575a 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 @@ -14,11 +14,6 @@ * limitations under the License. */ -@file:OptIn( - ExperimentalMaterial3Api::class, - ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class, -) - package io.element.android.features.messages.impl import androidx.activity.compose.BackHandler @@ -84,6 +79,7 @@ import io.element.android.libraries.designsystem.utils.LogCompositions import kotlinx.coroutines.launch import timber.log.Timber +@OptIn(ExperimentalMaterialApi::class) @Composable fun MessagesView( state: MessagesState, @@ -239,6 +235,7 @@ fun MessagesViewContent( } } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun MessagesViewTopBar( roomTitle: String?, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt index c2becf71cf..9dcabd8af7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalMaterialApi::class) - package io.element.android.features.messages.impl.actionlist import androidx.compose.foundation.clickable @@ -49,6 +47,7 @@ import io.element.android.libraries.designsystem.theme.components.ModalBottomShe import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch +@OptIn(ExperimentalMaterialApi::class) @Composable fun ActionListView( state: ActionListState, @@ -90,6 +89,7 @@ fun ActionListView( ) } +@OptIn(ExperimentalMaterialApi::class) @Composable private fun SheetContent( state: ActionListState, @@ -145,6 +145,7 @@ fun SheetContentLightPreview(@PreviewParameter(ActionListStateProvider::class) s fun SheetContentDarkPreview(@PreviewParameter(ActionListStateProvider::class) state: ActionListState) = ElementPreviewDark { ContentToPreview(state) } +@OptIn(ExperimentalMaterialApi::class) @Composable private fun ContentToPreview(state: ActionListState) { ActionListView( diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt index 0c0b46cbe2..9639759529 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.features.messages.textcomposer import app.cash.molecule.RecompositionClock @@ -52,7 +50,6 @@ import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor import io.element.android.libraries.textcomposer.MessageComposerMode import io.mockk.mockk import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest diff --git a/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingScreen.kt b/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingScreen.kt index 9fb54c7b45..8694865938 100644 --- a/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingScreen.kt +++ b/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingScreen.kt @@ -41,7 +41,6 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.HorizontalPager import com.google.accompanist.pager.HorizontalPagerIndicator import com.google.accompanist.pager.rememberPagerState @@ -53,9 +52,7 @@ import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import io.element.android.libraries.ui.strings.R as StringR -@OptIn(ExperimentalPagerApi::class) @Composable fun OnBoardingScreen( modifier: Modifier = Modifier, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt index 3a3ea3daa1..07facd77bd 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt @@ -54,7 +54,6 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser -@OptIn(ExperimentalMaterial3Api::class) @Composable fun RoomMemberListView( state: RoomMemberListState, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt index d41b8c28fc..acfc822658 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt @@ -116,7 +116,7 @@ fun RoomListView( } } -@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable fun RoomListContent( state: RoomListState, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt index 32bf30b082..f7fee35c75 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalMaterial3Api::class) - package io.element.android.features.roomlist.impl.components import androidx.activity.compose.BackHandler diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 40d89b7dd2..1138ad49d3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,7 +19,7 @@ activity = "1.7.1" startup = "1.1.1" # Compose -compose_bom = "2023.04.01" +compose_bom = "2023.05.01" composecompiler = "1.4.7" # Coroutines diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceScreen.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceScreen.kt index 531c18ed6b..03ba290829 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceScreen.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceScreen.kt @@ -45,7 +45,6 @@ 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 -@OptIn(ExperimentalMaterial3Api::class) @Composable fun PreferenceView( title: String, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/CenterAlignedTopAppBar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/CenterAlignedTopAppBar.kt index 5c2e741814..dab180e1c2 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/CenterAlignedTopAppBar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/CenterAlignedTopAppBar.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalMaterial3Api::class) - package io.element.android.libraries.designsystem.theme.components import androidx.compose.foundation.layout.RowScope diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt index 7f864bcb5c..d3cd2fee07 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalMaterial3Api::class) - package io.element.android.libraries.designsystem.theme.components import androidx.compose.foundation.layout.RowScope @@ -57,6 +55,7 @@ fun MediumTopAppBar( internal fun MediumTopAppBarPreview() = ElementThemedPreview { ContentToPreview() } +@OptIn(ExperimentalMaterial3Api::class) @Composable private fun ContentToPreview() { MediumTopAppBar(title = { Text(text = "Title") }) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheetLayout.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheetLayout.kt index 95d603b9dd..2f40a90615 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheetLayout.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheetLayout.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalMaterialApi::class, ExperimentalMaterialApi::class) - package io.element.android.libraries.designsystem.theme.components import androidx.compose.foundation.background @@ -107,6 +105,7 @@ internal fun ModalBottomSheetLayoutLightPreview() = internal fun ModalBottomSheetLayoutDarkPreview() = ElementPreviewDark { ContentToPreview() } +@OptIn(ExperimentalMaterialApi::class) @Composable private fun ContentToPreview() { ModalBottomSheetLayout( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedTextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedTextField.kt index 046dc5aa89..4bca15daca 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedTextField.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedTextField.kt @@ -49,7 +49,7 @@ import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.utils.allBooleans import io.element.android.libraries.designsystem.utils.asInt -@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable fun OutlinedTextField( value: String, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Scaffold.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Scaffold.kt index 99a69c0553..20e30e55b3 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Scaffold.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Scaffold.kt @@ -18,7 +18,6 @@ package io.element.android.libraries.designsystem.theme.components import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FabPosition import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ScaffoldDefaults @@ -27,7 +26,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -@OptIn(ExperimentalMaterial3Api::class) @Composable fun Scaffold( modifier: Modifier = Modifier, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SearchBar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SearchBar.kt index ca25501446..9a037cb7bb 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SearchBar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SearchBar.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalMaterial3Api::class) - package io.element.android.libraries.designsystem.theme.components import androidx.compose.foundation.interaction.MutableInteractionSource @@ -77,6 +75,7 @@ fun SearchBar( @Composable internal fun DockedSearchBarPreview() = ElementThemedPreview { ContentToPreview() } +@OptIn(ExperimentalMaterial3Api::class) @Composable private fun ContentToPreview() { SearchBar( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt index 602818f233..23848ef76d 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalMaterial3Api::class) - package io.element.android.libraries.designsystem.theme.components import androidx.compose.foundation.layout.RowScope 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 8709db67f0..7f1b78ebf7 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 @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.libraries.matrix.impl import io.element.android.libraries.core.coroutine.CoroutineDispatchers @@ -46,7 +44,6 @@ import io.element.android.libraries.matrix.impl.verification.RustSessionVerifica import io.element.android.libraries.sessionstorage.api.SessionStore import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.filter diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt index c908651e75..2574e96d3b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt @@ -91,7 +91,6 @@ internal class RustRoomSummaryDataSource( coroutineScope.cancel() } - //@OptIn(FlowPreview::class) override fun roomSummaries(): StateFlow> { return roomSummaries } diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt index 885a830b96..962cb2c18d 100644 --- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt +++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt @@ -43,9 +43,7 @@ fun DependencyHandlerScope.composeDependencies(libs: LibrariesForLibs) { androidTestImplementation(composeBom) implementation("androidx.compose.ui:ui") implementation("androidx.compose.material:material") - // Override BOM version, SearchBar is not available in the actual version - // do not use latest version because of clashes on androidx lifecycle dependency - implementation("androidx.compose.material3:material3:1.1.0-alpha04") + implementation("androidx.compose.material3:material3") implementation("androidx.compose.material:material-icons-extended") implementation("androidx.compose.ui:ui-tooling-preview") implementation(libs.androidx.activity.compose) diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt index 1accae8464..ef776c613c 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt @@ -14,15 +14,12 @@ * limitations under the License. */ -@file:OptIn(ExperimentalMaterial3Api::class) - package io.element.android.samples.minimal import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/TestCoroutineDispatchers.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/TestCoroutineDispatchers.kt index 1309a14cb1..e155eabd73 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/TestCoroutineDispatchers.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/TestCoroutineDispatchers.kt @@ -14,12 +14,9 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package io.element.android.tests.testutils import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestCoroutineScheduler import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.UnconfinedTestDispatcher From ac515afae33cea42f3ee078ae638ac3093993db5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 15 May 2023 13:24:27 +0200 Subject: [PATCH 13/18] Now that we have a CODEOWNERS file, there is no need for Renovate to assign a reviewer. It will be done automatically by GitHub. --- .github/renovate.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/renovate.json b/.github/renovate.json index 11f993516f..f9e1469496 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -4,7 +4,6 @@ "config:base" ], "labels": ["dependencies"], - "reviewers": ["team:element-x-android-reviewers"], "ignoreDeps": ["string:app_name"], "packageRules": [ { From e2f4ec3cf0eb629eb5fa9dc78e49676dd2dfbbde Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 15 May 2023 13:43:27 +0200 Subject: [PATCH 14/18] Fix more warning and replace deprecated code. Also use `TextField` from our components. --- .../features/roomlist/impl/search/RoomListSearch.kt | 11 ++++++----- .../theme/components/OutlinedTextField.kt | 8 +++----- .../designsystem/theme/components/TextField.kt | 6 ++---- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearch.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearch.kt index c2baca9a6b..021959c3aa 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearch.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearch.kt @@ -20,7 +20,6 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -30,7 +29,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.TextField import androidx.compose.material3.TextFieldDefaults import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable @@ -60,6 +58,7 @@ import io.element.android.libraries.designsystem.modifiers.applyIf 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.TextField import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.utils.copy import io.element.android.libraries.matrix.api.core.RoomId @@ -128,12 +127,14 @@ internal fun RoomListSearchResultContent( .focusRequester(focusRequester), value = filter, onValueChange = { state.eventSink(RoomListEvents.UpdateFilter(it)) }, - colors = TextFieldDefaults.textFieldColors( - containerColor = Color.Transparent, + colors = TextFieldDefaults.colors( + focusedContainerColor = Color.Transparent, + unfocusedContainerColor = Color.Transparent, + disabledContainerColor = Color.Transparent, focusedIndicatorColor = Color.Transparent, unfocusedIndicatorColor = Color.Transparent, + disabledIndicatorColor = Color.Transparent, errorIndicatorColor = Color.Transparent, - disabledIndicatorColor = Color.Transparent ), trailingIcon = { if (filter.isNotEmpty()) { diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedTextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedTextField.kt index 4bca15daca..e0edeed050 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedTextField.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedTextField.kt @@ -23,10 +23,9 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.TextFieldColors -import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.ExperimentalComposeUiApi @@ -49,7 +48,6 @@ import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.utils.allBooleans import io.element.android.libraries.designsystem.utils.asInt -@OptIn(ExperimentalMaterial3Api::class) @Composable fun OutlinedTextField( value: String, @@ -70,8 +68,8 @@ fun OutlinedTextField( singleLine: Boolean = false, maxLines: Int = Int.MAX_VALUE, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - shape: Shape = TextFieldDefaults.outlinedShape, - colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors() + shape: Shape = OutlinedTextFieldDefaults.shape, + colors: TextFieldColors = OutlinedTextFieldDefaults.colors() ) { androidx.compose.material3.OutlinedTextField( value = value, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField.kt index a4f07a61fb..54fe50b8bf 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField.kt @@ -23,7 +23,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.TextFieldColors import androidx.compose.material3.TextFieldDefaults @@ -50,7 +49,6 @@ import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.utils.allBooleans import io.element.android.libraries.designsystem.utils.asInt -@OptIn(ExperimentalMaterial3Api::class) @Composable fun TextField( value: String, @@ -71,8 +69,8 @@ fun TextField( singleLine: Boolean = false, maxLines: Int = Int.MAX_VALUE, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - shape: Shape = TextFieldDefaults.filledShape, - colors: TextFieldColors = TextFieldDefaults.textFieldColors() + shape: Shape = TextFieldDefaults.shape, + colors: TextFieldColors = TextFieldDefaults.colors() ) { androidx.compose.material3.TextField( value = value, From 8547d5f267881c3e692d1014db909cb530a4a4db Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 15 May 2023 13:50:21 +0200 Subject: [PATCH 15/18] Record screenshots. --- ...up_LabelledTextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...p_LabelledTextFieldLightPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ..._ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ..._ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...p_ChangeServerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...p_ChangeServerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...p_ChangeServerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...p_ChangeServerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...p_ChangeServerViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...p_ChangeServerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 ++-- ..._ChangeServerViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ..._ChangeServerViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ..._ChangeServerViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ..._ChangeServerViewLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ..._ChangeServerViewLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ..._ChangeServerViewLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...up_LoginRootScreenDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...up_LoginRootScreenDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...up_LoginRootScreenDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...up_LoginRootScreenDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...p_LoginRootScreenLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...p_LoginRootScreenLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...p_LoginRootScreenLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...p_LoginRootScreenLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...roup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...roup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...roup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...oup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...oup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...oup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...s_OutlinedTextFieldsDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...ields_OutlinedTextFieldsPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...TextFields_TextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...extFields_TextFieldLightPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- 36 files changed, 72 insertions(+), 72 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_LabelledTextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_LabelledTextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png index 2b77d948e4..7daf56c098 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_LabelledTextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_LabelledTextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f92e4287ae8db895d18452840a29240989597dea560640244b8188ee61e8c266 -size 30730 +oid sha256:57c89db393e87207bb9628244c435ee1541df327814d1ef7f296b80df8b8100c +size 30821 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_LabelledTextFieldLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_LabelledTextFieldLightPreview_0_null,NEXUS_5,1.0,en].png index c36b73a560..55b5f262b6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_LabelledTextFieldLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_LabelledTextFieldLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c66806646645afee1654b50818c539863e60129eba7fcd608e9801bd34d0fe30 -size 30028 +oid sha256:89bcb8fd5b3f68fbfc1568cd5e7d5c7bbabd3ca05836fbb1d8e75fbc32379317 +size 30222 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 699b8973c1..160def016f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:756d835de7820c96019995ab47d34c2663330c4ddc9ca124eac266ea9eeef0ab -size 64397 +oid sha256:24ad0b5edc27ddddca573b37f82579715d880793c0dc880248d2b572dd6d6a40 +size 64379 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 62e855dab1..c6a054a2d3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df7631ad85dc6b3fbdabfc820b11b4e12a6128202e3faa2d105ba22793fe8c22 -size 103428 +oid sha256:b0c9d4eaaaf6a691f43d25e0855ee056b69fa090ad8c53b26732c8f2532e2138 +size 103384 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index a9a3720ece..26129e266a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a03aa11c787804adc5b2947359421b90e6abcdc7e840983a0276a6e02da59a2f -size 58526 +oid sha256:4023481d2a045d7e0d824db81b28ee47c4bcdc568d98537b3573d554e0b76e32 +size 58445 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 4045dae649..af4d0b2046 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c2731ea21ca5b001aadc29ecbb15a2b263b0c042b7344cb10149174ff88ec0a -size 96835 +oid sha256:8f953d0df467ad4cd1ae24291e9593945b945712506f093bdcae3da975f8ca91 +size 96800 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 7aa5b9b9ba..f2249286f5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2fac7314c1dee8d4570922b984b0749f24ec8984e5e602892e2e4ed925d143b6 -size 41145 +oid sha256:c9e1058bff99d3193f3e69d7635a0446f76295ecdd47a16b03e39690a7a4852f +size 41232 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index bcae624eb5..5332196c0c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dff29249aabb7818151e55dbc9a1290bc09cf0e638e3ad2842791904b14368ca -size 44381 +oid sha256:42e2f813350feb63441492b67653fd22edb311d4e7068d32a42a537c9e78e94f +size 44632 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index ba0061da9b..eae8a2c125 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5450116320f6d75befbc784a31a8ad51c868936794e75e65bf24e6e1b69f1e23 -size 44421 +oid sha256:29c3ac70cd3f7a66d2be73996fe93825f850bb5a73312e9a0f2434a5e7003035 +size 44631 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 1bea49ba74..3f6519d314 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4363256d328a0ab2cc3966b42827c7a7319bee962710a0bac542f6494799ff71 -size 48526 +oid sha256:494167e5d7f96bf16b16f5b7c53b304dacf4015b31ce0cd51bfe9e05802664df +size 48831 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 6740abc12a..7f8690a106 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:afa9ab67471727f4c396d1919fd1187ec28a40b8cb9b03c2b35400c5e32f4eda -size 43776 +oid sha256:746b009cb527d5ed6be3c8b30bac9465c42970bd9766e259aee2957aaeb96da7 +size 44007 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 17f36f745e..d6ebbc9165 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b724d1b93a04117fc3cc405920fd22530e004cc9ba9c39f88ca1a2a2a4229c4 -size 43743 +oid sha256:cc677f36b2262eabc091023be5a373aaa51d47237fc6777d9b4950f16efa6ea2 +size 43964 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index d97cb348db..0827e7d026 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28c23058fdd9b24a7079251a2ab1ae2f85dafe0c1d4f01be3ffeca91984e0805 -size 37703 +oid sha256:ad6c6bdc7d4e4c405231c2c8944613ff151eb309e00821ac7eda107e853ed456 +size 37891 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 2520176bfd..e005e5910d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7779d6bf28eca6e72f9f1bcbd8b27881d0df0e594041269367a1f909e941cec9 -size 41368 +oid sha256:4cfd887060df1a1b7e12e3f47010e813678fec822595d90b19c51af99b42ab16 +size 41714 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index c48eee4f48..32208a1dde 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af77d0276f38c78524201c49484548e29d1c99168e68e11874c49f0db0803df2 -size 41392 +oid sha256:ede9377d3fbceae95a10e02cb1c033f9bbd53901137ef88ca52fdd4041737c70 +size 41695 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index d5c33e67c3..72944ac735 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:27d2d879493e8ed1cc17dcdbb384504126fad63eb7e53a069f530457c35b94b3 -size 47609 +oid sha256:6a976609ec6f5ce83388242db7a43ff6c062f84011ce7d2263e5ff8d23c64a96 +size 47912 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index 488e1403d4..2b16953c1c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66e9060ad93cec0e1bf8e284ac959a713a8201a33ff931aeb61aaf29ab7d9640 -size 40335 +oid sha256:e0a065c5a4c0af748524b0600e1b5233023cdff0eaae8a2039649323e8a19fe3 +size 40635 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index 1cc8419b21..bb4d824ee8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f7ecb805b73afe15db56853fec77d92f28fc9f8080dc7f1b2bb9f99cc262719 -size 40317 +oid sha256:dfa33ed990249e2d3e0097521bc51a3028bf51537736839a1301b54f99ad473b +size 40598 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 558ba024ca..1277ac6fa1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1501fd1372e15cd6f7693c4c854afcfd5ade3eb7a972f8cc76aea10de8416a20 -size 33382 +oid sha256:2bd0026073c5d0b7bb916bff9a034aca6393d005e299298f089f2f64d541f166 +size 33301 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 8341e4aa97..9919e584a6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:762a1d43dc3bc9edfa47f081b8385614f7075b5cecdd3eb3cb6b4959baf6811c -size 33445 +oid sha256:cbf81d780ebde2c089327df2b1397a6b879b9729dacefa78304e0d16e0a4dc11 +size 33318 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_4,NEXUS_5,1.0,en].png index a0dbfd3863..a73c22ff1a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7559f64cb74c33ef441dd15d557607061b72ffcdd3dce7ec7f8a663d928fd681 -size 29585 +oid sha256:4777def89f3b8717c8f9cba0023d33d8e244b3041270d283da28b813fd8f52ad +size 29606 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 558ba024ca..1277ac6fa1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1501fd1372e15cd6f7693c4c854afcfd5ade3eb7a972f8cc76aea10de8416a20 -size 33382 +oid sha256:2bd0026073c5d0b7bb916bff9a034aca6393d005e299298f089f2f64d541f166 +size 33301 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_2,NEXUS_5,1.0,en].png index d713681419..0ec9d0351d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68ef9478a9bbf361a7ace91b9214c6fca449a23b2756f0e0692e634f25c98181 -size 32385 +oid sha256:3eaf06ed9c04ad6b2d9314e7305b4c090cc5d78e6621628eed5c16a9e2738c0d +size 32196 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_3,NEXUS_5,1.0,en].png index 7140bc573f..bd0731900e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2fafe08ac635bf3f0e62753c832ead92de297f0826ec735efe1a1f90481cb421 -size 32417 +oid sha256:227596a612d3e87d4dd55ef43068875b1d50e334056b48e956e2708e5ed10645 +size 32220 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_4,NEXUS_5,1.0,en].png index a7010b4602..36576c6d42 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7954d3f7064666b287ce6ab05fb6814f0e4570db2db6a44437291f0f90d8e87f -size 29062 +oid sha256:beeb25a16dd5662be9b633b761fae145b91515859f87bda2399323c38cc0e561 +size 28995 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_5,NEXUS_5,1.0,en].png index d713681419..0ec9d0351d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68ef9478a9bbf361a7ace91b9214c6fca449a23b2756f0e0692e634f25c98181 -size 32385 +oid sha256:3eaf06ed9c04ad6b2d9314e7305b4c090cc5d78e6621628eed5c16a9e2738c0d +size 32196 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index a1f391b1b1..cd34f90d2b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d38eddd629e8c46993e616ed5bab2689fa5f24bb023ae43afca1a41ef5381ea5 -size 59843 +oid sha256:847c42b0aec8b12708f2a429bbbe2a86ca2b149b5963dd16135f3b34d9d0c073 +size 60153 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 2968e30033..07451a0330 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9630d9b5e3d26b677eaa8c0496d76d73f95d28a487ef0ef845d3a89747d6a8cb -size 179668 +oid sha256:628f1f00dca9d15faabd8288a3c54b1b8581a380cf14edf364f0dee9ecc187d5 +size 180117 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 2d89c7181f..87ba73daee 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dcff656fe8f2b1233dc55e5e4bc80800aa8ed511bb328b178f8944c83d4199a2 -size 59284 +oid sha256:43974ffd4bec4c2d8e07a6d4798f00db1aec316a11633c6d2eb5c6608deb694d +size 59586 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 3dba4f5dbc..a86c5980de 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60ee09b1052414f00af4d80dcf63c738d7dc937f5d58a3a043840cacc3618317 -size 58781 +oid sha256:18597d5e200b7079201cfa169ac3f9266c089b7dadde691c750192d2e22c1dea +size 59035 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 3292b422ee..8146c241cb 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f8ad5f69dbcda0284f731a01851fc73dc93ba9b1fad9e778b23f0155bfc7107 -size 178346 +oid sha256:827bccb0fdd3106fb24a95d665b4da2cfc15de48e8f508ae809c9f75d6d1bbf0 +size 178915 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 4470d22005..7aa17c8c18 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f01532b48a428362db2f963861a0ec5731ee3b1f13869a526facce3a85f82e8f -size 56677 +oid sha256:6bfb40e55460e4c11fdf0f020982b63e2b726a329902ceaa57522bafa328789d +size 56987 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_TextFields_OutlinedTextFieldsDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_TextFields_OutlinedTextFieldsDarkPreview_0_null,NEXUS_5,1.0,en].png index a1f82c98d8..a75c5f4399 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_TextFields_OutlinedTextFieldsDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_TextFields_OutlinedTextFieldsDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:02be8bb5887ed87aadc9758466cd4f3c622255fd32bccd347cedb70f509efa73 -size 49879 +oid sha256:916228ae8244eb2dd1602d6e3030890138d92c0775e1c8d8c11fcea810b32e6a +size 50153 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_TextFields_OutlinedTextFieldsPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_TextFields_OutlinedTextFieldsPreview_0_null,NEXUS_5,1.0,en].png index 29d860ddc9..2fe01fc5ff 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_TextFields_OutlinedTextFieldsPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_TextFields_OutlinedTextFieldsPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:38fdce46be1fd5acf8c8f9e23e59a1cae5f458daa191d7ba9fde6c929b380823 -size 45128 +oid sha256:914aecd8cef405ffd0c8d33b65d718ecad50060e95cb4b866de914a79c5ce6aa +size 45342 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_TextFields_TextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_TextFields_TextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png index 80f1a795af..c802637d6c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_TextFields_TextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_TextFields_TextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:505c2f4449cf297eec253c4a027b268e7f774bebd65dce94b651578b4659cb1f -size 55908 +oid sha256:52de83a9b21ff774b53db05f287fd76dcae248e6f509f8ff9873d1a252313dc2 +size 55177 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_TextFields_TextFieldLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_TextFields_TextFieldLightPreview_0_null,NEXUS_5,1.0,en].png index 70831eed9a..afe53b155a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_TextFields_TextFieldLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_TextFields_TextFieldLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f44ac6df2ab5199c0243c2b01e503912f2272657223abf1de22b359b2df2dfd -size 55733 +oid sha256:12283d22402cc6bb118e8ea5c2ef6c294f46e8c0f5d594ddfb374931e2717069 +size 55224 From b51c19af19198f01e549706aa3473d44ff3434f8 Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Mon, 15 May 2023 14:39:27 +0200 Subject: [PATCH 16/18] Show pending invitations in room members list (#385) Splits a Room's member list in 2 showing pending invitees first and then the actual room member. This simple user facing change entails a host of under the hood changes: - It copies the logic from the `userlist` module and merges it into the `roomdetails` module removing all details not related to the member list (e.g. gets rid of multiple selection, debouncing etc.). - Uncouples the `roomdetails` module from the `userlist` one. Now leaving only the `createroom` module to depend on the `userlist` module. Therefore the `userlist` module could be in the future completely removed and merged into the `createroom` module. - Changes the room members count in the room details screen to only show the members who have joined (i.e. don't count those still in the invited state). Missed ACs: - This change does not make the member list live update. Discussion is ongoing on how to make this technically feasible. Parent issue: - https://github.com/vector-im/element-x-android/issues/246 --- changelog.d/385.feature | 1 + features/roomdetails/impl/build.gradle.kts | 2 - .../roomdetails/impl/RoomDetailsPresenter.kt | 3 +- ...omMemberModules.kt => RoomMemberModule.kt} | 16 +- ...aSource.kt => RoomMemberListDataSource.kt} | 16 +- .../impl/members/RoomMemberListEvents.kt | 5 +- .../impl/members/RoomMemberListPresenter.kt | 71 ++++-- .../impl/members/RoomMemberListState.kt | 26 +- .../members/RoomMemberListStateProvider.kt | 94 ++++++-- .../impl/members/RoomMemberListView.kt | 228 +++++++++++++++--- .../roomdetails/RoomDetailsPresenterTests.kt | 25 +- .../members/RoomMemberListPresenterTests.kt | 127 +++++++--- .../RoomMemberDetailsPresenterTests.kt | 2 +- .../libraries/matrix/api/room/RoomMember.kt | 7 - ...stDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...stDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 3 + ...stDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 3 + ...stDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 3 + ...stDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 3 + ...stDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 3 + ...tLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...tLightPreview_0_null_2,NEXUS_5,1.0,en].png | 3 + ...tLightPreview_0_null_3,NEXUS_5,1.0,en].png | 3 + ...tLightPreview_0_null_4,NEXUS_5,1.0,en].png | 3 + ...tLightPreview_0_null_5,NEXUS_5,1.0,en].png | 3 + ...tLightPreview_0_null_6,NEXUS_5,1.0,en].png | 3 + 26 files changed, 477 insertions(+), 184 deletions(-) create mode 100644 changelog.d/385.feature rename features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/{RoomMemberModules.kt => RoomMemberModule.kt} (75%) rename features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/{RoomUserListDataSource.kt => RoomMemberListDataSource.kt} (75%) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_5,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_6,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_5,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_6,NEXUS_5,1.0,en].png diff --git a/changelog.d/385.feature b/changelog.d/385.feature new file mode 100644 index 0000000000..5b6cfbfff1 --- /dev/null +++ b/changelog.d/385.feature @@ -0,0 +1 @@ +Show pending invitations in room members list diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts index 41f117d6a4..7b072a5faf 100644 --- a/features/roomdetails/impl/build.gradle.kts +++ b/features/roomdetails/impl/build.gradle.kts @@ -40,7 +40,6 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.libraries.elementresources) implementation(projects.libraries.uiStrings) - implementation(projects.features.userlist.api) implementation(projects.libraries.androidutils) api(projects.features.roomdetails.api) implementation(libs.coil.compose) @@ -51,7 +50,6 @@ dependencies { testImplementation(libs.test.truth) testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.features.userlist.impl) testImplementation(projects.features.userlist.test) testImplementation(projects.tests.testutils) 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 8f96487583..81b2e1e383 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 @@ -34,6 +34,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState 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.RoomMembershipState import io.element.android.libraries.matrix.ui.room.getDirectRoomMember import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -124,7 +125,7 @@ class RoomDetailsPresenter @Inject constructor( MatrixRoomMembersState.Unknown -> Async.Uninitialized is MatrixRoomMembersState.Pending -> Async.Loading(prevState = membersState.prevRoomMembers?.size) is MatrixRoomMembersState.Error -> Async.Failure(membersState.failure, prevState = membersState.prevRoomMembers?.size) - is MatrixRoomMembersState.Ready -> Async.Success(membersState.roomMembers.size) + is MatrixRoomMembersState.Ready -> Async.Success(membersState.roomMembers.count { it.membership == RoomMembershipState.JOIN }) } } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModules.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt similarity index 75% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModules.kt rename to features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt index 2cd3b7eb08..ca462c6507 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModules.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt @@ -17,31 +17,17 @@ package io.element.android.features.roomdetails.impl.di import com.squareup.anvil.annotations.ContributesTo -import dagger.Binds import dagger.Module import dagger.Provides -import io.element.android.features.roomdetails.impl.members.RoomUserListDataSource import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter -import io.element.android.features.userlist.api.UserListDataSource import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom -import io.element.android.libraries.matrix.api.room.RoomMember -import javax.inject.Named @Module @ContributesTo(RoomScope::class) -interface RoomMemberBindsModule { - - @Binds - @Named("RoomMembers") - fun bindRoomMemberUserListDataSource(dataSource: RoomUserListDataSource): UserListDataSource -} - -@Module -@ContributesTo(RoomScope::class) -object RoomMemberProvidesModule { +object RoomMemberModule { @Provides fun provideRoomMemberDetailsPresenterFactory( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomUserListDataSource.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListDataSource.kt similarity index 75% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomUserListDataSource.kt rename to features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListDataSource.kt index b8c73526ed..5a9c30698b 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomUserListDataSource.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListDataSource.kt @@ -16,27 +16,23 @@ package io.element.android.features.roomdetails.impl.members -import io.element.android.features.userlist.api.UserListDataSource import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.roomMembers -import io.element.android.libraries.matrix.api.room.toMatrixUser -import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.coroutines.flow.dropWhile import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext import javax.inject.Inject -class RoomUserListDataSource @Inject constructor( +class RoomMemberListDataSource @Inject constructor( private val room: MatrixRoom, private val coroutineDispatchers: CoroutineDispatchers, -) : UserListDataSource { +) { - override suspend fun search(query: String): List = withContext(coroutineDispatchers.io) { + suspend fun search(query: String): List = withContext(coroutineDispatchers.io) { val roomMembers = room.membersStateFlow .dropWhile { it !is MatrixRoomMembersState.Ready } .first() @@ -50,11 +46,7 @@ class RoomUserListDataSource @Inject constructor( || member.displayName?.contains(query, ignoreCase = true).orFalse() } } - filteredMembers.map(RoomMember::toMatrixUser) - } - - override suspend fun getProfile(userId: UserId): MatrixUser? { - return null + filteredMembers } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListEvents.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListEvents.kt index 964a87eaf0..43716660eb 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListEvents.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListEvents.kt @@ -16,8 +16,7 @@ package io.element.android.features.roomdetails.impl.members -import io.element.android.libraries.matrix.api.user.MatrixUser - sealed interface RoomMemberListEvents { - data class SelectUser(val user: MatrixUser) : RoomMemberListEvents + data class UpdateSearchQuery(val query: String) : RoomMemberListEvents + data class OnSearchActiveChanged(val active: Boolean) : RoomMemberListEvents } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt index 0cc893f9e1..3a719983b6 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt @@ -18,54 +18,73 @@ package io.element.android.features.roomdetails.impl.members import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import io.element.android.features.userlist.api.SelectionMode -import io.element.android.features.userlist.api.UserListDataSource -import io.element.android.features.userlist.api.UserListDataStore -import io.element.android.features.userlist.api.UserListPresenter -import io.element.android.features.userlist.api.UserListPresenterArgs +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.matrix.api.room.MatrixRoom -import io.element.android.libraries.matrix.api.user.MatrixUser -import kotlinx.collections.immutable.ImmutableList +import io.element.android.libraries.matrix.api.room.RoomMembershipState import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.withContext import javax.inject.Inject -import javax.inject.Named class RoomMemberListPresenter @Inject constructor( - private val userListPresenterFactory: UserListPresenter.Factory, - @Named("RoomMembers") private val userListDataSource: UserListDataSource, - private val userListDataStore: UserListDataStore, - private val room: MatrixRoom, + private val roomMemberListDataSource: RoomMemberListDataSource, private val coroutineDispatchers: CoroutineDispatchers, ) : Presenter { - private val userListPresenter by lazy { - userListPresenterFactory.create( - UserListPresenterArgs(selectionMode = SelectionMode.Single), - userListDataSource, - userListDataStore, - ) - } - @Composable override fun present(): RoomMemberListState { - val userListState = userListPresenter.present() - val allUsers = remember { mutableStateOf>>(Async.Loading()) } + var roomMembers by remember { mutableStateOf>(Async.Loading()) } + var searchQuery by rememberSaveable { mutableStateOf("") } + var searchResults by remember { + mutableStateOf(RoomMemberSearchResultState.NotSearching) + } + var isSearchActive by rememberSaveable { mutableStateOf(false) } LaunchedEffect(Unit) { withContext(coroutineDispatchers.io) { - allUsers.value = Async.Success(userListDataSource.search("").toImmutableList()) + val members = roomMemberListDataSource.search("").groupBy { it.membership } + roomMembers = Async.Success( + RoomMembers( + invited = members.getOrDefault(RoomMembershipState.INVITE, emptyList()).toImmutableList(), + joined = members.getOrDefault(RoomMembershipState.JOIN, emptyList()).toImmutableList(), + ) + ) + } + } + + LaunchedEffect(searchQuery) { + withContext(coroutineDispatchers.io) { + searchResults = if (searchQuery.isEmpty()) { + RoomMemberSearchResultState.NotSearching + } else { + val results = roomMemberListDataSource.search(searchQuery).groupBy { it.membership } + if (results.isEmpty()) RoomMemberSearchResultState.NoResults + else RoomMemberSearchResultState.Results( + RoomMembers( + invited = results.getOrDefault(RoomMembershipState.INVITE, emptyList()).toImmutableList(), + joined = results.getOrDefault(RoomMembershipState.JOIN, emptyList()).toImmutableList(), + ) + ) + } } } return RoomMemberListState( - allUsers = allUsers.value, - userListState = userListState, + roomMembers = roomMembers, + searchQuery = searchQuery, + searchResults = searchResults, + isSearchActive = isSearchActive, + eventSink = { event -> + when (event) { + is RoomMemberListEvents.OnSearchActiveChanged -> isSearchActive = event.active + is RoomMemberListEvents.UpdateSearchQuery -> searchQuery = event.query + } + }, ) } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListState.kt index ef9d1f3839..a689ad1fd2 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListState.kt @@ -16,12 +16,30 @@ package io.element.android.features.roomdetails.impl.members -import io.element.android.features.userlist.api.UserListState import io.element.android.libraries.architecture.Async -import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.api.room.RoomMember import kotlinx.collections.immutable.ImmutableList data class RoomMemberListState( - val allUsers: Async>, - val userListState: UserListState, + val roomMembers: Async, + val searchQuery: String, + val searchResults: RoomMemberSearchResultState, + val isSearchActive: Boolean, + val eventSink: (RoomMemberListEvents) -> Unit, ) + +data class RoomMembers( + val invited: ImmutableList, + val joined: ImmutableList +) + +sealed interface RoomMemberSearchResultState { + /** No search results are available yet (e.g. because the user hasn't entered a (long enough) search term). */ + object NotSearching : RoomMemberSearchResultState + + /** The search has completed, but no results were found. */ + object NoResults : RoomMemberSearchResultState + + /** The search has completed, and some matching users were found. */ + data class Results(val results: RoomMembers) : RoomMemberSearchResultState +} diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt index a9babf57d8..7f91969bcd 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt @@ -17,27 +17,93 @@ package io.element.android.features.roomdetails.impl.members import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.userlist.api.UserSearchResultState -import io.element.android.features.userlist.api.aUserListState import io.element.android.libraries.architecture.Async -import io.element.android.libraries.matrix.api.user.MatrixUser -import io.element.android.libraries.matrix.ui.components.aMatrixUser -import kotlinx.collections.immutable.ImmutableList +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.room.RoomMember +import io.element.android.libraries.matrix.api.room.RoomMembershipState import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList internal class RoomMemberListStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( - aRoomMemberListState(allUsers = Async.Success(persistentListOf(aMatrixUser()))), - aRoomMemberListState(allUsers = Async.Loading()) + aRoomMemberListState( + roomMembers = Async.Success( + RoomMembers( + invited = persistentListOf(aVictor(), aWalter()), + joined = persistentListOf(anAlice(), aBob()), + ) + ) + ), + aRoomMemberListState(roomMembers = Async.Loading()), + aRoomMemberListState().copy(isSearchActive = false), + aRoomMemberListState().copy(isSearchActive = true), + aRoomMemberListState().copy(isSearchActive = true, searchQuery = "someone"), + aRoomMemberListState().copy( + isSearchActive = true, + searchQuery = "@someone:matrix.org", + searchResults = RoomMemberSearchResultState.Results( + RoomMembers( + invited = persistentListOf(aVictor()), + joined = persistentListOf(anAlice()), + ) + ), + ), + aRoomMemberListState().copy( + isSearchActive = true, + searchQuery = "something-with-no-results", + searchResults = RoomMemberSearchResultState.NoResults + ), ) } internal fun aRoomMemberListState( - searchResults: UserSearchResultState = UserSearchResultState.NotSearching, - allUsers: Async> = Async.Uninitialized, -) = - RoomMemberListState( - userListState = aUserListState().copy(searchResults = searchResults), - allUsers = allUsers, - ) + roomMembers: Async = Async.Uninitialized, + searchResults: RoomMemberSearchResultState = RoomMemberSearchResultState.NotSearching, +) = RoomMemberListState( + roomMembers = roomMembers, + searchQuery = "", + searchResults = searchResults, + isSearchActive = false, + eventSink = {} +) + +fun aRoomMember( + userId: UserId = UserId("@alice:server.org"), + displayName: String? = null, + avatarUrl: String? = null, + membership: RoomMembershipState = RoomMembershipState.JOIN, + isNameAmbiguous: Boolean = false, + powerLevel: Long = 0L, + normalizedPowerLevel: Long = 0L, + isIgnored: Boolean = false, +) = RoomMember( + userId = userId, + displayName = displayName, + avatarUrl = avatarUrl, + membership = membership, + isNameAmbiguous = isNameAmbiguous, + powerLevel = powerLevel, + normalizedPowerLevel = normalizedPowerLevel, + isIgnored = isIgnored, +) + +fun aRoomMemberList() = listOf( + anAlice(), + aBob(), + aRoomMember(UserId("@carol:server.org"), "Carol"), + aRoomMember(UserId("@david:server.org"), "David"), + aRoomMember(UserId("@eve:server.org"), "Eve"), + aRoomMember(UserId("@justin:server.org"), "Justin"), + aRoomMember(UserId("@mallory:server.org"), "Mallory"), + aRoomMember(UserId("@susie:server.org"), "Susie"), + aVictor(), + aWalter(), +) + +fun anAlice() = aRoomMember(UserId("@alice:server.org"), "Alice") +fun aBob() = aRoomMember(UserId("@bob:server.org"), "Bob") + +fun aVictor() = aRoomMember(UserId("@victor:server.org"), "Victor", membership = RoomMembershipState.INVITE) + +fun aWalter() = aRoomMember(UserId("@walter:server.org"), "Walter", membership = RoomMembershipState.INVITE) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt index 3a3ea3daa1..db38bbcf8b 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt @@ -16,20 +16,31 @@ package io.element.android.features.roomdetails.impl.members +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.Search import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SearchBarDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -39,37 +50,43 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.element.android.features.roomdetails.impl.R -import io.element.android.features.userlist.api.components.SearchSingleUserResultItem -import io.element.android.features.userlist.api.components.UserListView import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.isLoading import io.element.android.libraries.designsystem.ElementTextStyles +import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.theme.components.CenterAlignedTopAppBar import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator +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.SearchBar import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.ui.components.MatrixUserRow +import kotlinx.collections.immutable.ImmutableList +import io.element.android.libraries.ui.strings.R as StringR @OptIn(ExperimentalMaterial3Api::class) @Composable fun RoomMemberListView( state: RoomMemberListState, + onBackPressed: () -> Unit, + onMemberSelected: (UserId) -> Unit, modifier: Modifier = Modifier, - onBackPressed: () -> Unit = {}, - onMemberSelected: (UserId) -> Unit = {}, ) { - fun onUserSelected(user: MatrixUser) { - onMemberSelected(user.userId) + fun onUserSelected(roomMember: RoomMember) { + onMemberSelected(roomMember.userId) } Scaffold( topBar = { - if (!state.userListState.isSearchActive) { + if (!state.isSearchActive) { RoomMemberListTopBar(onBackPressed = onBackPressed) } } @@ -80,33 +97,26 @@ fun RoomMemberListView( .padding(padding), verticalArrangement = Arrangement.spacedBy(16.dp), ) { - UserListView( - state = state.userListState, - onUserSelected = ::onUserSelected, - ) + Column { + RoomMemberSearchBar( + query = state.searchQuery, + state = state.searchResults, + active = state.isSearchActive, + placeHolderTitle = stringResource(StringR.string.common_search_for_someone), + onActiveChanged = { state.eventSink(RoomMemberListEvents.OnSearchActiveChanged(it)) }, + onTextChanged = { state.eventSink(RoomMemberListEvents.UpdateSearchQuery(it)) }, + onUserSelected = ::onUserSelected, + modifier = Modifier.fillMaxWidth() + ) + } - if (!state.userListState.isSearchActive) { - if (state.allUsers is Async.Success) { - LazyColumn(modifier = Modifier.fillMaxWidth(), state = rememberLazyListState()) { - item { - val memberCount = state.allUsers.state.count() - Text( - modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp), - text = pluralStringResource(id = R.plurals.screen_room_member_list_header_title, count = memberCount, memberCount), - style = ElementTextStyles.Regular.callout, - color = MaterialTheme.colorScheme.secondary, - textAlign = TextAlign.Start, - ) - } - items(state.allUsers.state) { matrixUser -> - SearchSingleUserResultItem( - modifier = Modifier.fillMaxWidth(), - matrixUser = matrixUser, - onClick = { onUserSelected(matrixUser) } - ) - } - } - } else if (state.allUsers.isLoading()) { + if (!state.isSearchActive) { + if (state.roomMembers is Async.Success) { + RoomMemberList( + roomMembers = state.roomMembers.state, + onUserSelected = ::onUserSelected, + ) + } else if (state.roomMembers.isLoading()) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } @@ -116,9 +126,73 @@ fun RoomMemberListView( } } +@Composable +private fun RoomMemberList( + roomMembers: RoomMembers, + onUserSelected: (RoomMember) -> Unit, +) { + LazyColumn(modifier = Modifier.fillMaxWidth(), state = rememberLazyListState()) { + if (roomMembers.invited.isNotEmpty()) { + roomMemberListSection( + headerText = { stringResource(id = R.string.screen_room_member_list_pending_header_title) }, + members = roomMembers.invited, + onMemberSelected = { onUserSelected(it) } + ) + } + if (roomMembers.joined.isNotEmpty()) { + val memberCount = roomMembers.joined.count() + roomMemberListSection( + headerText = { pluralStringResource(id = R.plurals.screen_room_member_list_header_title, count = memberCount, memberCount) }, + members = roomMembers.joined, + onMemberSelected = { onUserSelected(it) } + ) + } + } +} + +private fun LazyListScope.roomMemberListSection( + headerText: @Composable () -> String, + members: ImmutableList, + onMemberSelected: (RoomMember) -> Unit, +) { + item { + Text( + modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp), + text = headerText(), + style = ElementTextStyles.Regular.callout, + color = MaterialTheme.colorScheme.secondary, + textAlign = TextAlign.Start, + ) + } + items(members) { matrixUser -> + RoomMemberListItem( + modifier = Modifier.fillMaxWidth(), + roomMember = matrixUser, + onClick = { onMemberSelected(matrixUser) } + ) + } +} + +@Composable +private fun RoomMemberListItem( + roomMember: RoomMember, + modifier: Modifier = Modifier, + onClick: () -> Unit = {}, +) { + MatrixUserRow( + modifier = modifier.clickable(onClick = onClick), + matrixUser = MatrixUser( + userId = roomMember.userId, + displayName = roomMember.displayName, + avatarUrl = roomMember.avatarUrl + ), + avatarSize = AvatarSize.Custom(36.dp), + ) +} + @OptIn(ExperimentalMaterial3Api::class) @Composable -fun RoomMemberListTopBar( +private fun RoomMemberListTopBar( modifier: Modifier = Modifier, onBackPressed: () -> Unit = {}, ) { @@ -135,6 +209,86 @@ fun RoomMemberListTopBar( ) } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun RoomMemberSearchBar( + query: String, + state: RoomMemberSearchResultState, + active: Boolean, + placeHolderTitle: String, + onActiveChanged: (Boolean) -> Unit, + onTextChanged: (String) -> Unit, + onUserSelected: (RoomMember) -> Unit, + modifier: Modifier = Modifier, +) { + val focusManager = LocalFocusManager.current + + if (!active) { + onTextChanged("") + focusManager.clearFocus() + } + + SearchBar( + query = query, + onQueryChange = onTextChanged, + onSearch = { focusManager.clearFocus() }, + active = active, + onActiveChange = onActiveChanged, + modifier = modifier + .padding(horizontal = if (!active) 16.dp else 0.dp), + placeholder = { + Text( + text = placeHolderTitle, + modifier = Modifier.alpha(0.4f), // FIXME align on Design system theme (removing alpha should be fine) + ) + }, + leadingIcon = if (active) { + { BackButton(onClick = { onActiveChanged(false) }) } + } else { + null + }, + trailingIcon = when { + active && query.isNotEmpty() -> { + { + IconButton(onClick = { onTextChanged("") }) { + Icon(Icons.Default.Close, stringResource(StringR.string.action_clear)) + } + } + } + + !active -> { + { + Icon( + imageVector = Icons.Default.Search, + contentDescription = stringResource(StringR.string.action_search), + modifier = Modifier.alpha(0.4f), // FIXME align on Design system theme (removing alpha should be fine) + ) + } + } + + else -> null + }, + colors = if (!active) SearchBarDefaults.colors() else SearchBarDefaults.colors(containerColor = Color.Transparent), + content = { + if (state is RoomMemberSearchResultState.Results) { + RoomMemberList( + roomMembers = state.results, + onUserSelected = { onUserSelected(it) } + ) + } else if (state is RoomMemberSearchResultState.NoResults) { + Spacer(Modifier.size(80.dp)) + + Text( + text = stringResource(StringR.string.common_no_results), + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.tertiary, + modifier = Modifier.fillMaxWidth() + ) + } + }, + ) +} + @Preview @Composable fun RoomMemberListLightPreview(@PreviewParameter(RoomMemberListStateProvider::class) state: RoomMemberListState) = @@ -147,5 +301,9 @@ fun RoomMemberListDarkPreview(@PreviewParameter(RoomMemberListStateProvider::cla @Composable private fun ContentToPreview(state: RoomMemberListState) { - RoomMemberListView(state) + RoomMemberListView( + state = state, + onBackPressed = {}, + onMemberSelected = {} + ) } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt index 2cc6d0ec24..b3717f4244 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt @@ -24,6 +24,8 @@ import io.element.android.features.roomdetails.impl.LeaveRoomWarning import io.element.android.features.roomdetails.impl.RoomDetailsEvent import io.element.android.features.roomdetails.impl.RoomDetailsPresenter import io.element.android.features.roomdetails.impl.RoomDetailsType +import io.element.android.features.roomdetails.impl.members.aRoomMember +import io.element.android.features.roomdetails.impl.members.aRoomMemberList import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.api.core.RoomId @@ -90,7 +92,7 @@ class RoomDetailsPresenterTests { val room = aMatrixRoom() val roomMembers = listOf( aRoomMember(A_USER_ID), - aRoomMember(A_USER_ID_2), + aRoomMember(A_USER_ID_2, membership = RoomMembershipState.INVITE), ) val presenter = aRoomDetailsPresenter(room) moleculeFlow(RecompositionClock.Immediate) { @@ -112,7 +114,7 @@ class RoomDetailsPresenterTests { room.givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers)) //skipItems(1) val successState = awaitItem() - Truth.assertThat(successState.memberCount).isEqualTo(Async.Success(roomMembers.size)) + Truth.assertThat(successState.memberCount).isEqualTo(Async.Success(1)) cancelAndIgnoreRemainingEvents() } @@ -266,22 +268,3 @@ fun aMatrixRoom( isDirect = isDirect, ) -fun aRoomMember( - userId: UserId = A_USER_ID, - displayName: String? = null, - avatarUrl: String? = null, - membership: RoomMembershipState = RoomMembershipState.JOIN, - isNameAmbiguous: Boolean = false, - powerLevel: Long = 0L, - normalizedPowerLevel: Long = 0L, - isIgnored: Boolean = false, -) = RoomMember( - userId = userId, - displayName = displayName, - avatarUrl = avatarUrl, - membership = membership, - isNameAmbiguous = isNameAmbiguous, - powerLevel = powerLevel, - normalizedPowerLevel = normalizedPowerLevel, - isIgnored = isIgnored, -) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt index 7eb650c937..48cf660877 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt @@ -20,62 +20,111 @@ import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth +import io.element.android.features.roomdetails.aMatrixRoom +import io.element.android.features.roomdetails.impl.members.RoomMemberListDataSource +import io.element.android.features.roomdetails.impl.members.RoomMemberListEvents import io.element.android.features.roomdetails.impl.members.RoomMemberListPresenter -import io.element.android.features.userlist.api.SelectionMode -import io.element.android.features.userlist.api.UserListDataSource -import io.element.android.features.userlist.api.UserListDataStore -import io.element.android.features.userlist.api.UserListPresenter -import io.element.android.features.userlist.api.UserListPresenterArgs -import io.element.android.features.userlist.api.UserSearchResultState -import io.element.android.features.userlist.impl.DefaultUserListPresenter -import io.element.android.features.userlist.test.FakeUserListDataSource +import io.element.android.features.roomdetails.impl.members.RoomMemberSearchResultState +import io.element.android.features.roomdetails.impl.members.aRoomMemberList +import io.element.android.features.roomdetails.impl.members.aVictor +import io.element.android.features.roomdetails.impl.members.aWalter import io.element.android.libraries.architecture.Async -import io.element.android.libraries.matrix.test.room.FakeMatrixRoom -import io.element.android.libraries.matrix.ui.components.aMatrixUser +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest -import okhttp3.internal.toImmutableList import org.junit.Test @ExperimentalCoroutinesApi class RoomMemberListPresenterTests { - private val testCoroutineDispatchers = testCoroutineDispatchers() - @Test - fun `present - search is done automatically on start, but is async`() = runTest { - val searchResult = listOf(aMatrixUser()) - val userListDataSource = FakeUserListDataSource().apply { - givenSearchResult(searchResult) - } - val userListDataStore = UserListDataStore() - val userListFactory = object : UserListPresenter.Factory { - override fun create( - args: UserListPresenterArgs, - userListDataSource: UserListDataSource, - userListDataStore: UserListDataStore, - ) = DefaultUserListPresenter(args, userListDataSource, userListDataStore) - } - val fakeRoom = FakeMatrixRoom() - val presenter = RoomMemberListPresenter( - userListPresenterFactory = userListFactory, - userListDataSource = userListDataSource, - userListDataStore = userListDataStore, - room = fakeRoom, - coroutineDispatchers = testCoroutineDispatchers - ) + fun `search is done automatically on start, but is async`() = runTest { + val presenter = createPresenter() moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.allUsers).isInstanceOf(Async.Loading::class.java) - Truth.assertThat(initialState.userListState.isSearchActive).isFalse() - Truth.assertThat(initialState.userListState.searchResults).isEqualTo(UserSearchResultState.NotSearching) - Truth.assertThat(initialState.userListState.selectionMode).isEqualTo(SelectionMode.Single) + Truth.assertThat(initialState.roomMembers).isInstanceOf(Async.Loading::class.java) + Truth.assertThat(initialState.searchQuery).isEmpty() + Truth.assertThat(initialState.searchResults).isEqualTo(RoomMemberSearchResultState.NotSearching) + Truth.assertThat(initialState.isSearchActive).isFalse() val loadedState = awaitItem() - Truth.assertThat((loadedState.allUsers as? Async.Success)?.state).isEqualTo(searchResult.toImmutableList()) + Truth.assertThat(loadedState.roomMembers).isInstanceOf(Async.Success::class.java) + Truth.assertThat((loadedState.roomMembers as Async.Success).state.invited).isEqualTo(listOf(aVictor(), aWalter())) + Truth.assertThat((loadedState.roomMembers as Async.Success).state.joined).isNotEmpty() + } + } + + @Test + fun `open search`() = runTest { + val presenter = createPresenter() + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + val loadedState = awaitItem() + + loadedState.eventSink(RoomMemberListEvents.OnSearchActiveChanged(true)) + + val searchActiveState = awaitItem() + Truth.assertThat((searchActiveState.isSearchActive)).isTrue() + } + } + + @Test + fun `search for something which is not found`() = runTest { + val presenter = createPresenter() + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + val loadedState = awaitItem() + loadedState.eventSink(RoomMemberListEvents.OnSearchActiveChanged(true)) + val searchActiveState = awaitItem() + loadedState.eventSink(RoomMemberListEvents.UpdateSearchQuery("something")) + val searchQueryUpdatedState = awaitItem() + Truth.assertThat((searchQueryUpdatedState.searchQuery)).isEqualTo("something") + val searchSearchResultDelivered = awaitItem() + Truth.assertThat((searchSearchResultDelivered.searchResults)).isInstanceOf(RoomMemberSearchResultState.NoResults::class.java) + } + } + + @Test + fun `search for something which is found`() = runTest { + val presenter = createPresenter() + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + val loadedState = awaitItem() + loadedState.eventSink(RoomMemberListEvents.OnSearchActiveChanged(true)) + val searchActiveState = awaitItem() + loadedState.eventSink(RoomMemberListEvents.UpdateSearchQuery("Alice")) + val searchQueryUpdatedState = awaitItem() + Truth.assertThat((searchQueryUpdatedState.searchQuery)).isEqualTo("Alice") + val searchSearchResultDelivered = awaitItem() + Truth.assertThat((searchSearchResultDelivered.searchResults)).isInstanceOf(RoomMemberSearchResultState.Results::class.java) + Truth.assertThat((searchSearchResultDelivered.searchResults as RoomMemberSearchResultState.Results).results.joined.first().displayName) + .isEqualTo("Alice") + } } } + +@ExperimentalCoroutinesApi +private fun createDataSource( + matrixRoom: MatrixRoom = aMatrixRoom().apply { + givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList())) + }, + coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers() +) = RoomMemberListDataSource(matrixRoom, coroutineDispatchers) + +@ExperimentalCoroutinesApi +private fun createPresenter( + roomMemberListDataSource: RoomMemberListDataSource = createDataSource(), + coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers() +) = RoomMemberListPresenter(roomMemberListDataSource, coroutineDispatchers) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt index 13eb28ca85..294b689ea9 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt @@ -22,7 +22,7 @@ import app.cash.turbine.test import com.google.common.truth.Truth import io.element.android.features.roomdetails.aMatrixClient import io.element.android.features.roomdetails.aMatrixRoom -import io.element.android.features.roomdetails.aRoomMember +import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsEvents import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt index 7c977f7569..3c9bd030b0 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt @@ -17,7 +17,6 @@ package io.element.android.libraries.matrix.api.room import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.api.user.MatrixUser data class RoomMember( val userId: UserId, @@ -30,12 +29,6 @@ data class RoomMember( val isIgnored: Boolean, ) -fun RoomMember.toMatrixUser() = MatrixUser( - userId = userId, - displayName = displayName, - avatarUrl = avatarUrl, -) - enum class RoomMembershipState { BAN, INVITE, JOIN, KNOCK, LEAVE } diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 27f36f9248..f96c3b07b9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6893108aabbf06f43af90f17b6b1a8a521312559f407647ce08db06ecb9a8f84 -size 22033 +oid sha256:29fa45284bef52648e02629a440b335595c7d4a4d04d0da5c4acbe9ae457bad6 +size 46918 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d03094bab0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49e7533e6afba903219942193e0cc5cfbe2f67ba3b57516f77c259e7c42e8e3f +size 11813 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f13a65f8e2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:89ea65099fb4981bbeb24e49afb7400b0e8da79e3b783e198519ba1e970404a8 +size 8317 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..74a7f599cd --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a8ba207cf61c56b64c6855d04c8a30def0b3daf325adf57edd45b40981ed745 +size 7733 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..31ca39b498 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92c4537ed8794f4db08b4edec74a0de41847c4fd8c4fe03b7112bc63124b0a61 +size 29326 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0f9a5a79cc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d5b879b74654fdad0638f432a71adfc9186fbe00dafd5d963a3affb33ec8c5c8 +size 12841 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_0,NEXUS_5,1.0,en].png index b493c070d2..c6b9c1b3ab 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e7b5bd916d4d3067b5013400b4ac864fab560e96fcb75ec895684021a00b8ba -size 21808 +oid sha256:3d40819700e3cbe9ca8ae1e5886de036a6dc05a7da6dd366490cc79c27ae3e8d +size 45653 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3d261cb40b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9c3abebbe9e55706af5f4d1089e4308b0e975258a9d6a0e1793a4208b36c9c93 +size 11754 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..eb23dcec96 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8de2f28cf9918a7eddcc1311c34ba0376242a4708724b57689df000b7480524 +size 8197 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e2e4a33e67 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:687133e4729ea42382ac0b76af6ceaf8b20cbc65923412fb97b077d2475c70f7 +size 7514 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4275020d4c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f663a70c43b9f2e66b651f17169b63859fe7c87c21312b61ed61a9136eabe5f +size 27989 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..787c71d87e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:534cba0c13aa52b3557ab8df854c521dd13b82e5a1550d73abcef12925e389da +size 11878 From b3c6d64fa30857c3d24b40b163fd8ad90b936dc8 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 15 May 2023 18:16:52 +0200 Subject: [PATCH 17/18] SDK - fix compilation --- .../libraries/matrix/impl/notification/NotificationMapper.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt index 079b1e0a5a..4b121db9bf 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.matrix.impl.notification +import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId @@ -36,7 +37,7 @@ class NotificationMapper @Inject constructor() { senderDisplayName = it.senderDisplayName, roomAvatarUrl = it.roomAvatarUrl, isDirect = it.isDirect, - isEncrypted = it.isEncrypted, + isEncrypted = it.isEncrypted.orFalse(), isNoisy = it.isNoisy ) } From c42154d66d80629920d63b9d1ea6f961258e1866 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 May 2023 17:32:55 +0000 Subject: [PATCH 18/18] Update dependency org.matrix.rustcomponents:sdk-android to v0.1.14 --- 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 7b343e4980..ec47b2eea0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -129,7 +129,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" } timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.13" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.14" sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" } sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" }