From 17365a50d17389c593f58624a9da6e20aba9172a Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Sun, 9 Aug 2026 01:59:17 +0300 Subject: [PATCH 001/112] Keep saving files to Downloads once 32 files share the same name MediaStore de-duplicates the display name of a new file itself, but its uniquifier gives up after 32 collisions and throws IllegalStateException. Saving then failed with "Sorry, an error occurred" and wrote nothing, and because image.png is the name most screenshots arrive with, a user who saves media regularly could reach a state where that name never saved again without deleting files from Downloads by hand. A new saveWithUniqueFileName helper in androidutils retries the insert once with a suffixed name when the platform reports it cannot build a unique one; a second failure propagates so genuine errors still surface. withFileNameSuffix builds that name by inserting the suffix before the extension, and handles names without one. Both call sites are fixed: AndroidLocalMediaActions and DefaultFileSave carried byte-identical logic and therefore the identical defect. While in AndroidLocalMediaActions, a null output uri from ContentResolver.insert now throws instead of reporting success without writing anything. The pre-Android-Q path is untouched: it overwrites rather than uniquifying, so it never reaches this limit. Relates to https://github.com/element-hq/element-x-android/issues/6371 --- .../features/viewfolder/impl/file/FileSave.kt | 24 ++--- .../libraries/androidutils/file/File.kt | 35 ++++++++ .../libraries/androidutils/file/FileTest.kt | 89 +++++++++++++++++++ .../impl/local/AndroidLocalMediaActions.kt | 24 ++--- 4 files changed, 150 insertions(+), 22 deletions(-) create mode 100644 libraries/androidutils/src/test/kotlin/io/element/android/libraries/androidutils/file/FileTest.kt diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/FileSave.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/FileSave.kt index 03f442f36e..add55cee36 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/FileSave.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/FileSave.kt @@ -16,6 +16,7 @@ import android.provider.MediaStore import androidx.annotation.RequiresApi import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding +import io.element.android.libraries.androidutils.file.saveWithUniqueFileName import io.element.android.libraries.androidutils.system.toast import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.extensions.runCatchingExceptions @@ -25,6 +26,7 @@ import kotlinx.coroutines.withContext import timber.log.Timber import java.io.File import java.io.FileOutputStream +import java.io.IOException interface FileSave { suspend fun save( @@ -61,18 +63,18 @@ class DefaultFileSave( @RequiresApi(Build.VERSION_CODES.Q) private fun saveOnDiskUsingMediaStore(path: String) { val file = File(path) - val contentValues = ContentValues().apply { - put(MediaStore.MediaColumns.DISPLAY_NAME, file.name) - put(MediaStore.MediaColumns.MIME_TYPE, MimeTypes.OctetStream) - put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) - } val resolver = context.contentResolver - val outputUri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues) - if (outputUri != null) { - file.inputStream().use { input -> - resolver.openOutputStream(outputUri).use { output -> - input.copyTo(output!!, DEFAULT_BUFFER_SIZE) - } + val outputUri = saveWithUniqueFileName(file.name) { displayName -> + val contentValues = ContentValues().apply { + put(MediaStore.MediaColumns.DISPLAY_NAME, displayName) + put(MediaStore.MediaColumns.MIME_TYPE, MimeTypes.OctetStream) + put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) + } + resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues) + } ?: throw IOException("Unable to create the destination file") + file.inputStream().use { input -> + resolver.openOutputStream(outputUri).use { output -> + input.copyTo(output!!, DEFAULT_BUFFER_SIZE) } } } 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 21306ff192..dd8dc4eeb0 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 @@ -42,6 +42,41 @@ fun File.safeRenameTo(dest: File) { ) } +/** + * Returns this file name with [suffix] inserted before its extension, i.e. `"image.png"` with the + * suffix `"_1"` gives `"image_1.png"`. A name without extension simply gets the suffix appended. + */ +fun String.withFileNameSuffix(suffix: String): String { + val extension = substringAfterLast('.', "") + return if (extension.isEmpty()) { + "$this$suffix" + } else { + "${substringBeforeLast('.')}$suffix.$extension" + } +} + +/** + * Runs [saveAs] with [fileName], and runs it once more with a suffixed name if the platform failed to + * build a unique name for it. + * + * `MediaStore` de-duplicates the display name of a new file itself, but gives up after 32 conflicts + * and throws `IllegalStateException("Failed to build unique file")`, which makes saving a file with a + * common name such as `image.png` fail for good. + * See https://github.com/element-hq/element-x-android/issues/6371 + */ +fun saveWithUniqueFileName( + fileName: String, + uniqueSuffix: () -> String = { "_${System.currentTimeMillis()}" }, + saveAs: (String) -> T, +): T { + return try { + saveAs(fileName) + } catch (nameConflict: IllegalStateException) { + Timber.w(nameConflict, "Unable to build a unique file name, retrying with a suffixed one") + saveAs(fileName.withFileNameSuffix(uniqueSuffix())) + } +} + fun Context.createTmpFile(baseDir: File = cacheDir, extension: String? = null): File { val suffix = extension?.let { ".$extension" } return File.createTempFile(UUID.randomUUID().toString(), suffix, baseDir).apply { mkdirs() } diff --git a/libraries/androidutils/src/test/kotlin/io/element/android/libraries/androidutils/file/FileTest.kt b/libraries/androidutils/src/test/kotlin/io/element/android/libraries/androidutils/file/FileTest.kt new file mode 100644 index 0000000000..b2e28f6249 --- /dev/null +++ b/libraries/androidutils/src/test/kotlin/io/element/android/libraries/androidutils/file/FileTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.androidutils.file + +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +class FileTest { + @Test + fun `withFileNameSuffix - inserts the suffix before the extension`() { + assertThat("image.png".withFileNameSuffix("_1")).isEqualTo("image_1.png") + } + + @Test + fun `withFileNameSuffix - appends the suffix when there is no extension`() { + assertThat("README".withFileNameSuffix("_1")).isEqualTo("README_1") + } + + @Test + fun `withFileNameSuffix - only considers the last extension`() { + assertThat("archive.tar.gz".withFileNameSuffix("_1")).isEqualTo("archive.tar_1.gz") + } + + @Test + fun `withFileNameSuffix - handles a name starting with a dot`() { + assertThat(".hidden".withFileNameSuffix("_1")).isEqualTo("_1.hidden") + } + + @Test + fun `saveWithUniqueFileName - uses the original name when it works`() { + val usedNames = mutableListOf() + + val result = saveWithUniqueFileName( + fileName = "image.png", + uniqueSuffix = { "_1" }, + ) { name -> + usedNames.add(name) + "saved as $name" + } + + assertThat(result).isEqualTo("saved as image.png") + assertThat(usedNames).containsExactly("image.png") + } + + @Test + fun `saveWithUniqueFileName - retries with a suffixed name when the name cannot be made unique`() { + val usedNames = mutableListOf() + + val result = saveWithUniqueFileName( + fileName = "image.png", + uniqueSuffix = { "_1" }, + ) { name -> + usedNames.add(name) + if (name == "image.png") { + error("Failed to build unique file") + } + "saved as $name" + } + + assertThat(result).isEqualTo("saved as image_1.png") + assertThat(usedNames).containsExactly("image.png", "image_1.png").inOrder() + } + + @Test + fun `saveWithUniqueFileName - does not retry more than once`() { + val usedNames = mutableListOf() + + val error = try { + saveWithUniqueFileName( + fileName = "image.png", + uniqueSuffix = { "_1" }, + ) { name -> + usedNames.add(name) + error("Failed to build unique file") + } + null + } catch (nameConflict: IllegalStateException) { + nameConflict + } + + assertThat(error).isInstanceOf(IllegalStateException::class.java) + assertThat(usedNames).containsExactly("image.png", "image_1.png").inOrder() + } +} diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt index 071e0dba64..4364020052 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt @@ -32,6 +32,7 @@ import androidx.core.content.PermissionChecker import androidx.core.net.toFile import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding +import io.element.android.libraries.androidutils.file.saveWithUniqueFileName import io.element.android.libraries.androidutils.system.startInstallFromSourceIntent import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.extensions.runCatchingExceptions @@ -44,6 +45,7 @@ import kotlinx.coroutines.withContext import timber.log.Timber import java.io.File import java.io.FileOutputStream +import java.io.IOException import java.io.InputStream @ContributesBinding(AppScope::class) @@ -172,18 +174,18 @@ class AndroidLocalMediaActions( @RequiresApi(Build.VERSION_CODES.Q) private fun saveOnDiskUsingMediaStore(localMedia: LocalMedia) { - val contentValues = ContentValues().apply { - put(MediaStore.MediaColumns.DISPLAY_NAME, localMedia.info.filename) - put(MediaStore.MediaColumns.MIME_TYPE, localMedia.info.mimeType) - put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) - } val resolver = context.contentResolver - val outputUri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues) - if (outputUri != null) { - localMedia.openStream()?.use { input -> - resolver.openOutputStream(outputUri).use { output -> - input.copyTo(output!!, DEFAULT_BUFFER_SIZE) - } + val outputUri = saveWithUniqueFileName(localMedia.info.filename) { displayName -> + val contentValues = ContentValues().apply { + put(MediaStore.MediaColumns.DISPLAY_NAME, displayName) + put(MediaStore.MediaColumns.MIME_TYPE, localMedia.info.mimeType) + put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) + } + resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues) + } ?: throw IOException("Unable to create the destination file") + localMedia.openStream()?.use { input -> + resolver.openOutputStream(outputUri).use { output -> + input.copyTo(output!!, DEFAULT_BUFFER_SIZE) } } } From fbdb99019e8735832cd097f8be02325495beb547 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Sun, 9 Aug 2026 15:40:20 +0300 Subject: [PATCH 002/112] Drop the MediaStore explanation from the save helper Review feedback: the retry itself is short enough to read, and the platform limit it works around belongs in the commit, not the source. The contract of the name helper keeps its one-line KDoc. --- .../element/android/libraries/androidutils/file/File.kt | 9 --------- 1 file changed, 9 deletions(-) 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 dd8dc4eeb0..7c5f0450e9 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 @@ -55,15 +55,6 @@ fun String.withFileNameSuffix(suffix: String): String { } } -/** - * Runs [saveAs] with [fileName], and runs it once more with a suffixed name if the platform failed to - * build a unique name for it. - * - * `MediaStore` de-duplicates the display name of a new file itself, but gives up after 32 conflicts - * and throws `IllegalStateException("Failed to build unique file")`, which makes saving a file with a - * common name such as `image.png` fail for good. - * See https://github.com/element-hq/element-x-android/issues/6371 - */ fun saveWithUniqueFileName( fileName: String, uniqueSuffix: () -> String = { "_${System.currentTimeMillis()}" }, From 4558590492a263b554d2a03a68195b5ce9171e34 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 10 Aug 2026 11:49:15 +0300 Subject: [PATCH 003/112] Cover both MediaStore save paths with a fake provider DefaultFileSave had no test at all and AndroidLocalMediaActions only exercised the failing path, so neither the uniquifying retry nor the new IOException on a null insert was ever executed. FakeMediaStoreContentProvider stands in for the media authority: it records the display names an insert is attempted with, keeps whatever is copied into it, and can either collide a name the way MediaStore does past 32 files or refuse to create anything. Both call sites now go through it for the plain save, the retry and the rejected insert. Its name cannot follow the Fake convention because it extends the framework ContentProvider, so it joins the existing exceptions in KonsistClassNameTest next to FakeFileSystem and FakeKeyStore. --- .../impl/file/DefaultFileSaveTest.kt | 60 ++++++++++++++++ .../local/AndroidLocalMediaActionsTest.kt | 41 +++++++++++ .../tests/konsist/KonsistClassNameTest.kt | 1 + .../fake/FakeMediaStoreContentProvider.kt | 72 +++++++++++++++++++ 4 files changed, 174 insertions(+) create mode 100644 features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/file/DefaultFileSaveTest.kt create mode 100644 tests/testutils/src/main/kotlin/io/element/android/tests/testutils/fake/FakeMediaStoreContentProvider.kt diff --git a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/file/DefaultFileSaveTest.kt b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/file/DefaultFileSaveTest.kt new file mode 100644 index 0000000000..a991a11f76 --- /dev/null +++ b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/file/DefaultFileSaveTest.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.viewfolder.impl.file + +import com.google.common.truth.Truth.assertThat +import io.element.android.tests.testutils.fake.registerFakeMediaStoreContentProvider +import io.element.android.tests.testutils.robolectric.RobolectricTest +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.robolectric.RuntimeEnvironment +import java.io.File + +class DefaultFileSaveTest : RobolectricTest() { + @Test + fun `save inserts the file in the Downloads collection under its own name`() = runTest { + val mediaStore = registerFakeMediaStoreContentProvider() + createFileSave().save(aFileToSave().path) + assertThat(mediaStore.insertedDisplayNames).containsExactly("a-file.txt") + assertThat(mediaStore.savedContent("a-file.txt")).isEqualTo("Some content") + } + + @Test + fun `save retries with a suffixed name when the name cannot be made unique`() = runTest { + val mediaStore = registerFakeMediaStoreContentProvider().apply { + conflictingDisplayNames = setOf("a-file.txt") + } + createFileSave().save(aFileToSave().path) + assertThat(mediaStore.insertedDisplayNames).hasSize(2) + assertThat(mediaStore.insertedDisplayNames.last()).matches("a-file_\\d+\\.txt") + assertThat(mediaStore.savedContent(mediaStore.insertedDisplayNames.last())).isEqualTo("Some content") + } + + @Test + fun `save writes nothing when the destination cannot be created`() = runTest { + val mediaStore = registerFakeMediaStoreContentProvider().apply { + returnsNoUriOnInsert = true + } + createFileSave().save(aFileToSave().path) + assertThat(mediaStore.insertedDisplayNames).containsExactly("a-file.txt") + assertThat(mediaStore.savedFileNames()).isEmpty() + } + + private fun aFileToSave(): File { + return File(RuntimeEnvironment.getApplication().cacheDir, "a-file.txt").apply { + writeText("Some content") + } + } + + private fun TestScope.createFileSave() = DefaultFileSave( + context = RuntimeEnvironment.getApplication(), + dispatchers = testCoroutineDispatchers(), + ) +} diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActionsTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActionsTest.kt index cdc5f7220d..ab1b193e26 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActionsTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActionsTest.kt @@ -12,18 +12,22 @@ import android.net.Uri import androidx.activity.compose.LocalActivityResultRegistryOwner import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.LocalContext +import androidx.core.net.toUri import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia +import io.element.android.tests.testutils.fake.registerFakeMediaStoreContentProvider import io.element.android.tests.testutils.robolectric.RobolectricTest import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test import org.robolectric.RuntimeEnvironment +import java.io.File +import java.io.IOException class AndroidLocalMediaActionsTest : RobolectricTest() { @Test @@ -62,6 +66,43 @@ class AndroidLocalMediaActionsTest : RobolectricTest() { assertThat(result.exceptionOrNull()).isNotNull() } + @Test + fun `test AndroidLocalMediaAction save on disk inserts the media under its own name`() = runTest { + val mediaStore = registerFakeMediaStoreContentProvider() + val sut = createAndroidLocalMediaActions() + val result = sut.saveOnDisk(aLocalMedia(aMediaFile().toUri())) + assertThat(result.exceptionOrNull()).isNull() + assertThat(mediaStore.insertedDisplayNames).containsExactly("an image file.jpg") + } + + @Test + fun `test AndroidLocalMediaAction save on disk retries with a suffixed name when the name cannot be made unique`() = runTest { + val mediaStore = registerFakeMediaStoreContentProvider().apply { + conflictingDisplayNames = setOf("an image file.jpg") + } + val sut = createAndroidLocalMediaActions() + val result = sut.saveOnDisk(aLocalMedia(aMediaFile().toUri())) + assertThat(result.exceptionOrNull()).isNull() + assertThat(mediaStore.insertedDisplayNames).hasSize(2) + assertThat(mediaStore.insertedDisplayNames.last()).matches("an image file_\\d+\\.jpg") + } + + @Test + fun `test AndroidLocalMediaAction save on disk fails when the destination cannot be created`() = runTest { + registerFakeMediaStoreContentProvider().apply { + returnsNoUriOnInsert = true + } + val sut = createAndroidLocalMediaActions() + val result = sut.saveOnDisk(aLocalMedia(aMediaFile().toUri())) + assertThat(result.exceptionOrNull()).isInstanceOf(IOException::class.java) + } + + private fun aMediaFile(): File { + return File(RuntimeEnvironment.getApplication().cacheDir, "an image file.jpg").apply { + writeText("Some content") + } + } + private fun TestScope.createAndroidLocalMediaActions() = AndroidLocalMediaActions( RuntimeEnvironment.getApplication(), testCoroutineDispatchers(), diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt index 93b3200acc..ddb5ba95b3 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt @@ -104,6 +104,7 @@ class KonsistClassNameTest { "FakeImageLoader", "FakeKeyStore", "FakeListenableFuture", + "FakeMediaStoreContentProvider", ) .assertTrue { val interfaceName = it.name diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/fake/FakeMediaStoreContentProvider.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/fake/FakeMediaStoreContentProvider.kt new file mode 100644 index 0000000000..4a1ffcda29 --- /dev/null +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/fake/FakeMediaStoreContentProvider.kt @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.tests.testutils.fake + +import android.content.ContentProvider +import android.content.ContentValues +import android.content.pm.ProviderInfo +import android.database.Cursor +import android.net.Uri +import android.os.ParcelFileDescriptor +import android.provider.MediaStore +import androidx.test.core.app.ApplicationProvider +import org.robolectric.shadows.ShadowContentResolver +import java.io.File +import java.nio.file.Files + +/** + * Stands in for the MediaStore provider: it records the display names an insert is attempted with, + * writes what is copied into it to a temporary directory, and can make any display name collide the + * way MediaStore does once a directory already holds 32 files with the same name. + */ +class FakeMediaStoreContentProvider : ContentProvider() { + private val directory: File = Files.createTempDirectory("fake-media-store").toFile() + + val insertedDisplayNames = mutableListOf() + + var conflictingDisplayNames: Set = emptySet() + + /** When true, an insert reports that it created nothing, the way a rejected insert does. */ + var returnsNoUriOnInsert: Boolean = false + + fun savedFileNames(): List = directory.list().orEmpty().toList() + + fun savedContent(displayName: String): String = File(directory, displayName).readText() + + override fun onCreate() = true + + override fun insert(uri: Uri, values: ContentValues?): Uri? { + val displayName = values?.getAsString(MediaStore.MediaColumns.DISPLAY_NAME).orEmpty() + insertedDisplayNames += displayName + check(displayName !in conflictingDisplayNames) { "Failed to build unique file" } + return if (returnsNoUriOnInsert) null else uri.buildUpon().appendPath(displayName).build() + } + + override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor { + val file = File(directory, uri.lastPathSegment.orEmpty()) + return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_CREATE or ParcelFileDescriptor.MODE_READ_WRITE) + } + + override fun query(uri: Uri, projection: Array?, selection: String?, selectionArgs: Array?, sortOrder: String?): Cursor? = null + + override fun getType(uri: Uri): String? = null + + override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array?) = 0 + + override fun delete(uri: Uri, selection: String?, selectionArgs: Array?) = 0 +} + +fun registerFakeMediaStoreContentProvider(): FakeMediaStoreContentProvider { + return FakeMediaStoreContentProvider().apply { + attachInfo( + ApplicationProvider.getApplicationContext(), + ProviderInfo().apply { authority = MediaStore.AUTHORITY } + ) + ShadowContentResolver.registerProviderInternal(MediaStore.AUTHORITY, this) + } +} From 59b26173a750f450d5f85ec4888de57e0abcde97 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Thu, 13 Aug 2026 17:36:51 +0300 Subject: [PATCH 004/112] Create the fake MediaStore directory without java.nio.file Files.createTempDirectory needs API 26 and the fake lives in the main source set of tests:testutils, so lint failed it against the minimum of 24. File.createTempFile has been available since API 1 and still gives each provider instance its own directory. --- .../tests/testutils/fake/FakeMediaStoreContentProvider.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/fake/FakeMediaStoreContentProvider.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/fake/FakeMediaStoreContentProvider.kt index 4a1ffcda29..13077db51f 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/fake/FakeMediaStoreContentProvider.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/fake/FakeMediaStoreContentProvider.kt @@ -17,7 +17,6 @@ import android.provider.MediaStore import androidx.test.core.app.ApplicationProvider import org.robolectric.shadows.ShadowContentResolver import java.io.File -import java.nio.file.Files /** * Stands in for the MediaStore provider: it records the display names an insert is attempted with, @@ -25,7 +24,10 @@ import java.nio.file.Files * way MediaStore does once a directory already holds 32 files with the same name. */ class FakeMediaStoreContentProvider : ContentProvider() { - private val directory: File = Files.createTempDirectory("fake-media-store").toFile() + private val directory: File = File.createTempFile("fake-media-store", null).apply { + delete() + mkdir() + } val insertedDisplayNames = mutableListOf() From fcecc7acba6d7820de69c5faecf3545483c13c8b Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:33:10 +0300 Subject: [PATCH 005/112] Give up on a verification request the other session never accepts The outgoing verification flow had no client side timeout: if the other session never answered, the screen waited on "Waiting to accept request" for as long as the user left it open, with no indication that the request could no longer succeed. Bound the wait to the ten minutes the Matrix specification gives a verification request. Past that the other session can no longer accept it, so cancel the request and move to the canceled state, which already offers to start again. --- .../OutgoingVerificationStateMachine.kt | 17 +++++++++--- .../OutgoingVerificationPresenterTest.kt | 26 ++++++++++++++++--- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateMachine.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateMachine.kt index 465fc20066..2fd7948c38 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateMachine.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateMachine.kt @@ -23,9 +23,11 @@ import io.element.android.libraries.matrix.api.verification.SessionVerificationS import io.element.android.libraries.matrix.api.verification.VerificationRequest import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.timeout +import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds import com.freeletics.flowredux.dsl.State as MachineState @@ -36,6 +38,11 @@ class OutgoingVerificationStateMachine( ) : FlowReduxStateMachine( initialState = State.Initial, ) { + companion object { + /** Lifetime the Matrix specification gives a verification request; past it the other session can no longer accept ours. */ + private val VERIFICATION_REQUEST_TIMEOUT = 10.minutes + } + init { spec { inState { @@ -44,11 +51,15 @@ class OutgoingVerificationStateMachine( } } inState { - onEnterEffect { event -> - when (event.verificationRequest) { + onEnter { state -> + val verificationRequest = state.snapshot.verificationRequest + when (verificationRequest) { is VerificationRequest.Outgoing.CurrentSession -> sessionVerificationService.requestDeviceVerification() - is VerificationRequest.Outgoing.User -> sessionVerificationService.requestUserVerification(event.verificationRequest.userId) + is VerificationRequest.Outgoing.User -> sessionVerificationService.requestUserVerification(verificationRequest.userId) } + delay(VERIFICATION_REQUEST_TIMEOUT) + sessionVerificationService.cancelVerification() + state.override { State.Canceled.andLogStateChange() } } on { _, state -> state.override { State.VerificationRequestAccepted.andLogStateChange() } diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt index c6cbfca281..bf3f690a07 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt @@ -6,8 +6,6 @@ * Please see LICENSE files in the repository root for full details. */ -@file:Suppress("UnusedImports") - package io.element.android.features.verifysession.impl.outgoing import app.cash.turbine.ReceiveTurbine @@ -30,10 +28,12 @@ import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.test import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test +import kotlin.time.Duration.Companion.minutes @ExperimentalCoroutinesApi class OutgoingVerificationPresenterTest { @@ -140,6 +140,26 @@ class OutgoingVerificationPresenterTest { } } + @Test + fun `present - A verification request the other session never accepts is canceled`() = runTest { + val cancelVerificationRecorder = lambdaRecorder {} + val service = unverifiedSessionService( + requestDeviceVerificationLambda = { }, + cancelVerificationLambda = cancelVerificationRecorder, + ) + val presenter = createOutgoingVerificationPresenter(service) + presenter.test { + awaitItem().eventSink(OutgoingVerificationViewEvents.RequestVerification) + advanceTimeBy(1.minutes) + assertThat(awaitItem().step).isEqualTo(Step.AwaitingOtherDeviceResponse) + advanceTimeBy(9.minutes) + expectNoEvents() + advanceTimeBy(2.minutes) + assertThat(awaitItem().step).isEqualTo(Step.Canceled) + cancelVerificationRecorder.assertions().isCalledOnce() + } + } + @Test fun `present - Canceling the flow once it's verifying cancels it`() = runTest { val service = unverifiedSessionService( @@ -283,7 +303,7 @@ class OutgoingVerificationPresenterTest { var state = awaitItem() assertThat(state.step).isEqualTo(Step.Initial) state.eventSink(OutgoingVerificationViewEvents.RequestVerification) - testScope.advanceUntilIdle() + testScope.advanceTimeBy(1.minutes) // Await for other device response: fakeService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest) state = awaitItem() From 5bfb614bdb370410d3aaa825eab2c4f3e3895cf1 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Mon, 17 Aug 2026 15:37:11 +0300 Subject: [PATCH 006/112] Restore the UnusedImports suppression on the outgoing verification test ktlint reports the TestScope import as unused because the type only appears in a context parameter, which the rule does not look at. The file carried a file-level suppression for exactly that before this branch dropped it. --- .../impl/outgoing/OutgoingVerificationPresenterTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt index bf3f690a07..f872f495b3 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt @@ -6,6 +6,8 @@ * Please see LICENSE files in the repository root for full details. */ +@file:Suppress("UnusedImports") + package io.element.android.features.verifysession.impl.outgoing import app.cash.turbine.ReceiveTurbine From 2ff6a56e7df3e4642f7ffc81416b95df2a24cf77 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Wed, 19 Aug 2026 16:12:15 +0000 Subject: [PATCH 007/112] Use the new SDK thread info for redacted messages --- .../impl/timeline/groups/Groupability.kt | 2 +- .../impl/timeline/TimelinePresenterTest.kt | 4 +- .../factories/TimelineItemsFactoryTest.kt | 40 +++++++++++++++++++ .../RedactedVoiceMessageManagerTest.kt | 4 +- .../DefaultPinnedMessagesBannerFormatter.kt | 2 +- .../impl/DefaultRoomLatestEventFormatter.kt | 2 +- .../impl/DefaultTimelineEventFormatter.kt | 2 +- ...efaultPinnedMessagesBannerFormatterTest.kt | 2 +- .../DefaultRoomLatestEventFormatterTest.kt | 2 +- .../api/timeline/item/event/EventContent.kt | 4 +- .../timeline/item/event/EventTimelineItem.kt | 1 + .../item/event/TimelineEventContentMapper.kt | 2 +- .../matrix/test/timeline/TimelineFixture.kt | 5 +++ .../reply/InReplyToDetailsProvider.kt | 2 +- .../messages/reply/InReplyToMetadataKtTest.kt | 2 +- .../impl/datasource/EventItemFactory.kt | 2 +- .../impl/datasource/EventItemFactoryTest.kt | 4 +- 17 files changed, 65 insertions(+), 17 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt index 431942378a..3b16f79f79 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt @@ -91,7 +91,7 @@ internal fun MatrixTimelineItem.Event.canBeDisplayedInBubbleBlock(): Boolean { // Can be grouped is FailedToParseMessageLikeContent, is MessageContent, - RedactedContent, + is RedactedContent, is StickerContent, is PollContent, is UnableToDecryptContent, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt index 3b0794305f..0db690f928 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt @@ -47,7 +47,6 @@ import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction import io.element.android.libraries.matrix.api.timeline.item.event.ReactionSender import io.element.android.libraries.matrix.api.timeline.item.event.Receipt -import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem import io.element.android.libraries.matrix.test.AN_EVENT_ID @@ -65,6 +64,7 @@ import io.element.android.libraries.matrix.test.room.aRoomMember import io.element.android.libraries.matrix.test.room.powerlevels.FakeRoomPermissions import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.matrix.test.timeline.aMessageContent +import io.element.android.libraries.matrix.test.timeline.aRedactedContent import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import io.element.android.libraries.matrix.test.timeline.item.event.aRoomMembershipContent import io.element.android.libraries.matrix.ui.components.aMatrixUserList @@ -876,7 +876,7 @@ class TimelinePresenterTest { (0 until 3).map { index -> MatrixTimelineItem.Event( uniqueId = UniqueId("redacted_$index"), - event = anEventTimelineItem(eventId = EventId("\$R$index"), content = RedactedContent), + event = anEventTimelineItem(eventId = EventId("\$R$index"), content = aRedactedContent()), ) } ), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt index 53c39ee530..094fa5764e 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt @@ -12,10 +12,14 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactory import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition +import io.element.android.features.messages.impl.timeline.model.TimelineItemThreadInfo import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.EventThreadInfo +import io.element.android.libraries.matrix.test.A_THREAD_ID import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.timeline.aMessageContent +import io.element.android.libraries.matrix.test.timeline.aRedactedContent import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest @@ -82,6 +86,42 @@ class TimelineItemsFactoryTest { ).inOrder() } + @Test + fun `redacted message keeps its thread info`() = runTest { + val factory = aTimelineItemsFactory( + config = TimelineItemsFactoryConfig( + computeReadReceipts = false, + computeReactions = false, + ) + ) + val items = listOf( + MatrixTimelineItem.Event( + uniqueId = UniqueId("event-0"), + event = anEventTimelineItem( + sender = A_USER_ID, + timestamp = 0L, + content = aRedactedContent( + threadInfo = EventThreadInfo.ThreadResponse(A_THREAD_ID), + ), + ), + ) + ) + var threadInfo: TimelineItemThreadInfo? = null + factory.timelineItems.test { + factory.replaceWith( + timelineItems = items, + roomMembers = emptyList(), + renderReadReceipts = false, + ) + threadInfo = awaitItem() + .filterIsInstance() + .firstOrNull() + ?.threadInfo + cancelAndIgnoreRemainingEvents() + } + assertThat(threadInfo).isEqualTo(TimelineItemThreadInfo.ThreadResponse(A_THREAD_ID)) + } + private suspend fun TestScope.groupPositionsOf(timestamps: List): List { val factory = aTimelineItemsFactory( config = TimelineItemsFactoryConfig( diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt index a1ffd1f02b..9a5eda366b 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt @@ -16,11 +16,11 @@ import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.ProfileDetails -import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.AN_EVENT_ID_2 import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.core.FakeSendHandle +import io.element.android.libraries.matrix.test.timeline.aRedactedContent import io.element.android.libraries.mediaplayer.api.MediaPlayer import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer import io.element.android.tests.testutils.testCoroutineDispatchers @@ -88,7 +88,7 @@ fun aRedactedMatrixTimeline(eventId: EventId) = listOf( sender = A_USER_ID, senderProfile = ProfileDetails.Unavailable, timestamp = 9442, - content = RedactedContent, + content = aRedactedContent(), origin = null, timelineItemDebugInfoProvider = { TimelineItemDebugInfo( diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt index 1311c524e4..8025591070 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt @@ -56,7 +56,7 @@ class DefaultPinnedMessagesBannerFormatter( is PollContent -> { content.question.prefixWith(CommonStrings.a11y_poll) } - RedactedContent -> { + is RedactedContent -> { sp.getString(CommonStrings.common_message_removed) } else -> { diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt index e7bbdb1e7f..bd519b60c2 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt @@ -89,7 +89,7 @@ class DefaultRoomLatestEventFormatter( ): CharSequence? { return when (content) { is MessageContent -> content.process(senderDisambiguatedDisplayName, isDmRoom, isOutgoing) - RedactedContent -> { + is RedactedContent -> { val message = sp.getString(CommonStrings.common_message_removed) message.prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing) } diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt index ff5cce7a59..ffb98fe2ec 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt @@ -62,7 +62,7 @@ class DefaultTimelineEventFormatter( is CallNotifyContent -> { sp.getString(CommonStrings.common_call_started) } - RedactedContent, + is RedactedContent, is LegacyCallInviteContent, is StickerContent, is PollContent, diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt index 198da2bf7d..38517dab33 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt @@ -81,7 +81,7 @@ class DefaultPinnedMessagesBannerFormatterTest : RobolectricTest() { fun `Redacted content`() { val expected = "Message removed" val senderName = "Someone" - val message = createRoomEvent(false, senderName, RedactedContent) + val message = createRoomEvent(false, senderName, RedactedContent(threadInfo = null)) val result = formatter.format(message) assertThat(result).isEqualTo(expected) } diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt index ecd0509269..c293b67975 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt @@ -85,7 +85,7 @@ class DefaultRoomLatestEventFormatterTest : RobolectricTest() { val expected = "Message removed" val senderName = "Someone" sequenceOf(false, true).forEach { isDm -> - val message = createLatestEvent(false, senderName, RedactedContent) + val message = createLatestEvent(false, senderName, RedactedContent(threadInfo = null)) val result = formatter.format(message, isDm) if (isDm) { assertThat(result).isEqualTo(expected) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt index c2d388ecb0..132b3b3504 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt @@ -32,7 +32,9 @@ data class MessageContent( val type: MessageType, ) : EventContent -data object RedactedContent : EventContent +data class RedactedContent( + val threadInfo: EventThreadInfo?, +) : EventContent data class StickerContent( val filename: String, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt index 91703f7fa4..8a3221677d 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt @@ -51,6 +51,7 @@ data class EventTimelineItem( is PollContent -> content.threadInfo is StickerContent -> content.threadInfo is UnableToDecryptContent -> content.threadInfo + is RedactedContent -> content.threadInfo else -> null } 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 98efa505d0..c8434e207a 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 @@ -81,7 +81,7 @@ class TimelineEventContentMapper( ) } is MsgLikeKind.Redacted -> { - RedactedContent + RedactedContent(threadInfo = extractThreadInfo(it.content)) } is MsgLikeKind.Poll -> { PollContent( diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt index b61009a5c6..da8b872ef0 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt @@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.PollContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileDetails import io.element.android.libraries.matrix.api.timeline.item.event.Receipt +import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent import io.element.android.libraries.matrix.api.timeline.item.event.SendHandleProvider import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType @@ -137,6 +138,10 @@ fun aStickerContent( threadInfo = threadInfo, ) +fun aRedactedContent( + threadInfo: EventThreadInfo? = null, +) = RedactedContent(threadInfo = threadInfo) + fun aTimelineItemDebugInfo( model: String = "Rust(Model())", originalJson: String? = null, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt index b335ef9609..0b7db84ae1 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt @@ -117,7 +117,7 @@ class InReplyToDetailsDisambiguatedProvider : InReplyToDetailsProvider() { class InReplyToDetailsInformativeProvider : InReplyToDetailsProvider() { override val values: Sequence get() = sequenceOf( - RedactedContent, + RedactedContent(threadInfo = null), UnableToDecryptContent(data = UnableToDecryptContent.Data.Unknown, threadInfo = null), ).map { aInReplyToDetails( diff --git a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt index 3fcb2f8f18..64e647ab19 100644 --- a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt +++ b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt @@ -656,7 +656,7 @@ class InReplyToMetadataKtTest : RobolectricTest() { fun `redacted content`() = runTest { moleculeFlow(RecompositionMode.Immediate) { anInReplyToDetailsReady( - eventContent = RedactedContent + eventContent = RedactedContent(threadInfo = null) ).metadata(hideImage = false) }.test { awaitItem().let { diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactory.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactory.kt index 40a9a0351d..f140fc6628 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactory.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactory.kt @@ -79,7 +79,7 @@ class EventItemFactory( LegacyCallInviteContent, is PollContent, is ProfileChangeContent, - RedactedContent, + is RedactedContent, is RoomMembershipContent, is StateContent, is StickerContent, diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactoryTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactoryTest.kt index 4ed89e8923..4f02637a62 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactoryTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactoryTest.kt @@ -35,7 +35,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessa import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType import io.element.android.libraries.matrix.api.timeline.item.event.OtherMessageType import io.element.android.libraries.matrix.api.timeline.item.event.OtherState -import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent import io.element.android.libraries.matrix.api.timeline.item.event.StateContent import io.element.android.libraries.matrix.api.timeline.item.event.StickerMessageType import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType @@ -49,6 +48,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.timeline.aMessageContent import io.element.android.libraries.matrix.test.timeline.aPollContent import io.element.android.libraries.matrix.test.timeline.aProfileChangeMessageContent +import io.element.android.libraries.matrix.test.timeline.aRedactedContent import io.element.android.libraries.matrix.test.timeline.aStickerContent import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import io.element.android.libraries.matrix.test.timeline.item.event.aRoomMembershipContent @@ -80,7 +80,7 @@ class EventItemFactoryTest { LegacyCallInviteContent, aPollContent(), aProfileChangeMessageContent(), - RedactedContent, + aRedactedContent(), aRoomMembershipContent( userId = A_USER_ID, ), From 8a32e3d4bfbaea20868822ed08b2c1d3aba12026 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Fri, 21 Aug 2026 16:40:49 +0300 Subject: [PATCH 008/112] Order the outgoing verification state machine imports ktlint's import-ordering rule expects lexicographic order within the first group, so TimeoutCancellationException has to precede delay. --- .../impl/outgoing/OutgoingVerificationStateMachine.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateMachine.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateMachine.kt index 130dca3eb6..05851437cc 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateMachine.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateMachine.kt @@ -22,8 +22,8 @@ import io.element.android.libraries.matrix.api.verification.SessionVerificationS import io.element.android.libraries.matrix.api.verification.VerificationRequest import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.delay import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.timeout From feb87ead8d740ac89cfafc60a82ed09ef7ef0332 Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Mon, 24 Aug 2026 08:59:11 -0700 Subject: [PATCH 009/112] Make the account provider screen scrollable so its description is not clipped by the keyboard --- .../confirmaccountprovider/ConfirmAccountProviderView.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt index 1dab5cd31c..3ade999bfc 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt @@ -115,6 +115,9 @@ fun ConfirmAccountProviderView( HeaderFooterPage( modifier = modifier, + // Let the header and content scroll so the supporting text is not clipped by the footer when the + // keyboard is visible and vertical space is reduced. + isScrollable = true, header = { IconTitleSubtitleMolecule( modifier = Modifier.padding(top = 60.dp), From 9586478039fec2d39697c5b759364d83cebe522b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Aug 2026 18:58:54 +0200 Subject: [PATCH 010/112] Updates to text sizes in Chats header and Space hero Closes #7476 --- .../home/impl/components/HomeTopBar.kt | 103 ++++++++++-------- .../atomic/atoms/RoomPreviewTitleAtom.kt | 4 +- .../ui/components/SpaceHeaderRootView.kt | 2 +- .../matrix/ui/components/SpaceHeaderView.kt | 9 +- 4 files changed, 67 insertions(+), 51 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt index 63d28dca21..1a351be9b6 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt @@ -80,7 +80,6 @@ import io.element.android.libraries.designsystem.modifiers.backgroundVerticalGra import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE -import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.DropdownMenu import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem import io.element.android.libraries.designsystem.theme.components.Icon @@ -141,15 +140,23 @@ fun HomeTopBar( else -> stringResource(selectedNavigationItem.labelRes) } } - HomeNavigationBarItem.Spaces -> stringResource(selectedNavigationItem.labelRes) + HomeNavigationBarItem.Spaces -> null + } + displayTitle?.let { + val style = when (spaceFiltersState) { + // Space name + is SpaceFiltersState.Selected -> ElementTheme.typography.fontHeadingSmMedium + // "Chats" + else -> ElementTheme.typography.fontHeadingLgBold + } + Text( + modifier = Modifier.semantics { + heading() + }, + style = style, + text = displayTitle, + ) } - Text( - modifier = Modifier.semantics { - heading() - }, - style = ElementTheme.typography.aliasScreenTitle, - text = displayTitle, - ) }, navigationIcon = { NavigationIcon( @@ -179,7 +186,9 @@ fun HomeTopBar( TopAppBarScrollBehaviorLayout(scrollBehavior = scrollBehavior) { RoomListFiltersView( state = filtersState, - modifier = Modifier.padding(bottom = 16.dp).padding(contentPadding) + modifier = Modifier + .padding(bottom = 16.dp) + .padding(contentPadding) ) } } @@ -348,45 +357,45 @@ private fun AccountIcon( ), contentAlignment = Alignment.Center, ) { - val avatarData by remember(matrixUser) { - derivedStateOf { - matrixUser.getAvatarData(size = AvatarSize.CurrentUserTopBar) - } + val avatarData by remember(matrixUser) { + derivedStateOf { + matrixUser.getAvatarData(size = AvatarSize.CurrentUserTopBar) } - val statusEmoji = matrixUser.displayedStatus?.toEmojiText() - val avatarModifier = if (statusEmoji != null) { - Modifier.eraseStatusEmojiBackground( - parentSize = AvatarSize.CurrentUserTopBar.dp, - layoutDirection = LocalLayoutDirection.current, - ) - } else { - Modifier - } - Avatar( - avatarData = avatarData, - avatarType = AvatarType.User, - modifier = avatarModifier, - contentDescription = if (isCurrentAccount) { - if (showAvatarIndicator) { - stringResource(CommonStrings.a11y_settings_with_required_action) - } else { - stringResource(CommonStrings.common_settings) - } - } else { - null - }, + } + val statusEmoji = matrixUser.displayedStatus?.toEmojiText() + val avatarModifier = if (statusEmoji != null) { + Modifier.eraseStatusEmojiBackground( + parentSize = AvatarSize.CurrentUserTopBar.dp, + layoutDirection = LocalLayoutDirection.current, ) - if (statusEmoji != null) { - StatusEmojiBadge( - emoji = statusEmoji, - modifier = Modifier.align(Alignment.BottomEnd), - ) - } - if (showAvatarIndicator) { - RedIndicatorAtom( - modifier = Modifier.align(Alignment.TopEnd) - ) - } + } else { + Modifier + } + Avatar( + avatarData = avatarData, + avatarType = AvatarType.User, + modifier = avatarModifier, + contentDescription = if (isCurrentAccount) { + if (showAvatarIndicator) { + stringResource(CommonStrings.a11y_settings_with_required_action) + } else { + stringResource(CommonStrings.common_settings) + } + } else { + null + }, + ) + if (statusEmoji != null) { + StatusEmojiBadge( + emoji = statusEmoji, + modifier = Modifier.align(Alignment.BottomEnd), + ) + } + if (showAvatarIndicator) { + RedIndicatorAtom( + modifier = Modifier.align(Alignment.TopEnd) + ) + } } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoomPreviewTitleAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoomPreviewTitleAtom.kt index 740605fe88..4188827a8a 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoomPreviewTitleAtom.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoomPreviewTitleAtom.kt @@ -10,6 +10,7 @@ package io.element.android.libraries.designsystem.atomic.atoms import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.style.TextAlign import io.element.android.compound.theme.ElementTheme @@ -20,11 +21,12 @@ fun RoomPreviewTitleAtom( title: String, modifier: Modifier = Modifier, fontStyle: FontStyle? = null, + style: TextStyle = ElementTheme.typography.fontHeadingLgBold ) { Text( modifier = modifier, text = title, - style = ElementTheme.typography.fontHeadingLgBold, + style = style, textAlign = TextAlign.Center, fontStyle = fontStyle, color = ElementTheme.colors.textPrimary, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderRootView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderRootView.kt index 1e8fb864a7..2b5e8c6fce 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderRootView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderRootView.kt @@ -46,7 +46,7 @@ fun SpaceHeaderRootView( ) Text( text = stringResource(CommonStrings.screen_space_list_title), - style = ElementTheme.typography.fontHeadingLgBold, + style = ElementTheme.typography.fontHeadingMdBold, color = ElementTheme.colors.textPrimary, textAlign = TextAlign.Center, ) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderView.kt index 7aee62902f..8a833cee44 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderView.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.tooling.preview.datasource.LoremIpsum import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewAliasAtom import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewDescriptionAtom import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewTitleAtom @@ -68,11 +69,15 @@ fun SpaceHeaderView( }, title = { if (name != null) { - RoomPreviewTitleAtom(title = name) + RoomPreviewTitleAtom( + title = name, + style = ElementTheme.typography.fontHeadingMdBold, + ) } else { RoomPreviewTitleAtom( title = stringResource(id = CommonStrings.common_no_space_name), - fontStyle = FontStyle.Italic + fontStyle = FontStyle.Italic, + style = ElementTheme.typography.fontHeadingMdBold, ) } }, From 95866f960eccb722414800f1cb57e891e74b2d0a Mon Sep 17 00:00:00 2001 From: ElementBot Date: Mon, 24 Aug 2026 17:16:01 +0000 Subject: [PATCH 011/112] Update screenshots --- ...s.home.impl.components_HomeTopBarMultiAccount_Day_0_en.png | 4 ++-- ...home.impl.components_HomeTopBarMultiAccount_Night_0_en.png | 4 ++-- ...eatures.home.impl.components_HomeTopBarSpaces_Day_0_en.png | 4 ++-- ...tures.home.impl.components_HomeTopBarSpaces_Night_0_en.png | 4 ++-- ....home.impl.components_HomeTopBarWithIndicator_Day_0_en.png | 4 ++-- ...ome.impl.components_HomeTopBarWithIndicator_Night_0_en.png | 4 ++-- ...res.home.impl.components_HomeTopBarWithStatus_Day_0_en.png | 4 ++-- ...s.home.impl.components_HomeTopBarWithStatus_Night_0_en.png | 4 ++-- .../features.home.impl.components_HomeTopBar_Day_0_en.png | 4 ++-- .../features.home.impl.components_HomeTopBar_Night_0_en.png | 4 ++-- .../features.home.impl.spaces_HomeSpacesView_Day_0_en.png | 4 ++-- .../features.home.impl.spaces_HomeSpacesView_Day_1_en.png | 4 ++-- .../features.home.impl.spaces_HomeSpacesView_Night_0_en.png | 4 ++-- .../features.home.impl.spaces_HomeSpacesView_Night_1_en.png | 4 ++-- .../snapshots/images/features.home.impl_HomeViewA11y_en.png | 4 ++-- .../snapshots/images/features.home.impl_HomeView_Day_0_en.png | 4 ++-- .../images/features.home.impl_HomeView_Day_10_en.png | 4 ++-- .../images/features.home.impl_HomeView_Day_11_en.png | 4 ++-- .../images/features.home.impl_HomeView_Day_13_en.png | 4 ++-- .../images/features.home.impl_HomeView_Day_14_en.png | 4 ++-- .../images/features.home.impl_HomeView_Day_15_en.png | 4 ++-- .../images/features.home.impl_HomeView_Day_16_en.png | 4 ++-- .../snapshots/images/features.home.impl_HomeView_Day_1_en.png | 4 ++-- .../snapshots/images/features.home.impl_HomeView_Day_2_en.png | 4 ++-- .../snapshots/images/features.home.impl_HomeView_Day_3_en.png | 4 ++-- .../snapshots/images/features.home.impl_HomeView_Day_4_en.png | 4 ++-- .../snapshots/images/features.home.impl_HomeView_Day_5_en.png | 4 ++-- .../snapshots/images/features.home.impl_HomeView_Day_6_en.png | 4 ++-- .../snapshots/images/features.home.impl_HomeView_Day_7_en.png | 4 ++-- .../snapshots/images/features.home.impl_HomeView_Day_8_en.png | 4 ++-- .../snapshots/images/features.home.impl_HomeView_Day_9_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_0_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_10_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_11_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_13_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_14_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_15_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_16_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_1_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_2_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_3_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_4_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_5_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_6_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_7_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_8_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_9_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Day_0_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Day_1_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Day_2_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Day_3_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Day_4_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Day_5_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Night_0_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Night_1_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Night_2_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Night_3_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Night_4_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Night_5_en.png | 4 ++-- ...ries.matrix.ui.components_SpaceHeaderRootView_Day_0_en.png | 4 ++-- ...es.matrix.ui.components_SpaceHeaderRootView_Night_0_en.png | 4 ++-- ...ibraries.matrix.ui.components_SpaceHeaderView_Day_0_en.png | 4 ++-- ...raries.matrix.ui.components_SpaceHeaderView_Night_0_en.png | 4 ++-- 63 files changed, 126 insertions(+), 126 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarMultiAccount_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarMultiAccount_Day_0_en.png index d68c73c3dd..5f17df2b70 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarMultiAccount_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarMultiAccount_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:32ad73a97cbf59783c6f5ba2ba216c180af3b8ff30280b712b9b936293a2ecf1 -size 22034 +oid sha256:16eff51daa024855e4843c78ac5d8f381e0ca66d7fd4102319ae74bc5298d4bd +size 22758 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarMultiAccount_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarMultiAccount_Night_0_en.png index 3df4db4b5f..a381f7a081 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarMultiAccount_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarMultiAccount_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13fac4328987e056ff03284d8d8814d762e8134ea8e3c33ccd95036d1c662b16 -size 20306 +oid sha256:41a251131b2d236b7e5d24730def468e9bf1861409dd1a7ef8eaf9c3b5ab2226 +size 20909 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarSpaces_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarSpaces_Day_0_en.png index 690fad4569..0f3475b8ce 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarSpaces_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarSpaces_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:499d48e33c188e42e066e30644d2ce4d1a2516b6b26d15c385d613234777d738 -size 12105 +oid sha256:a6b9a6700cc66e74dced47f658cc8a6bff533e29e026dfada6c733180b72a19b +size 9126 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarSpaces_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarSpaces_Night_0_en.png index 82f1da2494..c5e641590e 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarSpaces_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarSpaces_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f9d7c7900c41fbc10b27a68bf079e4750c28cfdcc4d96db35a79f3606cd5796 -size 11205 +oid sha256:e4db5dc9d61a6c3e7f19d79efe8b247e91f77d04885b1b63a86682137d009023 +size 8232 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithIndicator_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithIndicator_Day_0_en.png index 2a515ca368..1fd8dfd36f 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithIndicator_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithIndicator_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd697e25338f15e3dfeb82370f1726cc337fa74a5e65240cad22c404ec949976 -size 22485 +oid sha256:d5accca8cea12a266d137db293d48261e7bb92ae84e38167dc287ed39a34df5c +size 23207 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithIndicator_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithIndicator_Night_0_en.png index 7dd2daea6e..91b11e062a 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithIndicator_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithIndicator_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0567d8419972054d7dce9a080b856324a1bc97529ff2d15776aba6c023eb90e2 -size 20730 +oid sha256:3bc4c15cdee0758dfb100efb9c2571152940f0a7724b8e3838ef223c0bd122c8 +size 21325 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithStatus_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithStatus_Day_0_en.png index 4fa1c78188..54b10704ee 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithStatus_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithStatus_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd896bfc1111dcb04d94584c19a395e69612dd9ceda8beea6432c14aa4f18023 -size 23126 +oid sha256:6a38ac7b1ae8a91f08a8010d684204474e815fb967bbabe3a1080d152d802d44 +size 23742 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithStatus_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithStatus_Night_0_en.png index 9111455bec..a41049657f 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithStatus_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBarWithStatus_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:262622cd1124902ab45ccd645d33193dd8d0e4a99ba7e4d2cff786393b350118 -size 21299 +oid sha256:520288193ac12637e44d96ce99ca2f44d447699ae840674c146ec05fe41493d1 +size 21902 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBar_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBar_Day_0_en.png index 539216dd6b..495b8b894b 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBar_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBar_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1f98cbd7fb3856f01738bca57088ee72c859aef8f09ae8f597b7769ef372ed7 -size 22144 +oid sha256:1981c2ba67db5027a3dad0a477dd33ee2947044574c2b89a55cf7972ab64b3bf +size 22863 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBar_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBar_Night_0_en.png index 353ee965dc..2b154c8425 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBar_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_HomeTopBar_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:038445401184aa84312acebbcc52327202ca0613a88d2667591db00b0121b287 -size 20378 +oid sha256:b31e258cd95a44e39943224285751a1b0ca51b9d490dc70479bf11bb63385dae +size 20974 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_0_en.png index c69cba2125..fb9ed3f101 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:09c0613d19abd7673223bc5eac12b4e87c73e66e7ff9d437c5f97ae8bcaf9303 -size 85239 +oid sha256:43b2800a5fb8014557801f35210a98752529c3c71ec285e95be1d405d2910327 +size 84602 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_1_en.png index bd9fa30de2..82f5491b50 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df0dc04b3946a58dda5c3499d965ab23cd3c172eedcb8a23461201c2aca95934 -size 38713 +oid sha256:9640707e06cf5ac5b4f60305c2166b3562f072bcfa13b56b1ed8e6bdd446d24e +size 37854 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_0_en.png index 8ec9b1585e..f1a441a0b3 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e28f120a919db7c7a0ced5ca0d56239197785a0ecfbd37af0c5dacb5f8316c1f -size 83583 +oid sha256:330103af450379bc1ea3bd9815779324384d6025a68ba8e3bb9a76b2565fa2dc +size 82909 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_1_en.png index 72e173c493..29f23a6b80 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc37ee6efade5947d0fdc25f6925cf45ba55442eb3fb715f942178513a00e8fc -size 37416 +oid sha256:8d6216bd19d1fae4b5cb70aefe2f3959a18d08f0e1f0ae82e174c10427df400f +size 36596 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeViewA11y_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeViewA11y_en.png index aca66273b4..fbb9365c88 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeViewA11y_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeViewA11y_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9cf305c0e52881fb40fee2ba7bdcdc3dfa5c899577b3e252af19e37ee2efacf7 -size 143213 +oid sha256:610593d1acf3fa39f937f787a5e6531c3dd0a2262bc1abf63b8489f227917ba5 +size 144141 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_0_en.png index b5f80f2faf..091b77a218 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8247dedc9811f2b325568973a2b714cd4b46322bcd0bf89af0e662518fa77747 -size 67694 +oid sha256:265d1fd77f393724a26a410e343b1dde4ce8e986db16ba00bb07def24aa52139 +size 68363 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_10_en.png index f106244405..e2b3622d86 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c393b13532fba4d4ef25dda45b6ca3e1f6da6d49c5b2de412beded261a4f9311 -size 36164 +oid sha256:3c3e85ad3d267db2fa0964b4d28ef9fd50ce4989259646d5d07bdea7e24c7a4c +size 36872 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_11_en.png index f2a69ea475..48b80d3c0c 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b493bc5151f15d6ba91bfb0c27b41f3abcf13ee7d511aa59db171cecdbd95f3 -size 31958 +oid sha256:3c3580427877cca8f59538bccd47ee19f57b4bdce28d60135da7de185e17f8f1 +size 32636 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_13_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_13_en.png index f4dff45422..80e9263c7e 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_13_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a33681fe064d5e12f53273bfd9262af8c7b902e237cb8d0641573b0645e291c -size 94687 +oid sha256:7758cf4023fa542f29fd8ade54bab1f2707666c640ad62ed8a6805c8ec2256a3 +size 95336 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_14_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_14_en.png index bdad242551..2d5574b88a 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_14_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_14_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:022954bdca89572b74bc70f6d91820e99eef8826409c0a06e6145a5c3ec8eaf4 -size 87268 +oid sha256:60eec8085688c228b28cef3f1922b984f2cdc54a9207ea04c27c44900fb4d7ee +size 87932 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_15_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_15_en.png index edf07a66f3..0f310c78a8 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_15_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_15_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af36d0e3383935d256b3730e6f955e858ae42a82f54485b879e7a6a71fc25f6c -size 55237 +oid sha256:0f2280adb5d99a20fbe98b8d097e97c2de0c05a083382a905075bfb5795a2290 +size 55890 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_16_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_16_en.png index aa313049f5..559a928a97 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_16_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_16_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e55407ac7116b3127a979f630777d0d528a349c38d37829d8bc0c37a316c7c5b -size 42526 +oid sha256:7a694440a4ac9db68a24ef77f0502b3936370974b1f6932c49accb6736eb5566 +size 39875 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_1_en.png index b5f80f2faf..091b77a218 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8247dedc9811f2b325568973a2b714cd4b46322bcd0bf89af0e662518fa77747 -size 67694 +oid sha256:265d1fd77f393724a26a410e343b1dde4ce8e986db16ba00bb07def24aa52139 +size 68363 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_2_en.png index b5f80f2faf..091b77a218 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8247dedc9811f2b325568973a2b714cd4b46322bcd0bf89af0e662518fa77747 -size 67694 +oid sha256:265d1fd77f393724a26a410e343b1dde4ce8e986db16ba00bb07def24aa52139 +size 68363 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_3_en.png index e01335907a..f4db0cfcf2 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:25e5ac35ddf79fdc78c21dbd031f506da04caac7bdd53f279c88fd4574f72a82 -size 64644 +oid sha256:564360123154706aceed6daf43f33c96c7e3c49b787222ecc236aabb1349e3a1 +size 65323 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_4_en.png index fe41a54fcc..9eb0a6a43d 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8c1245da0a4cf56aba8e102e7f297996ed58fc8c50165288dea11ca86fbc0dcc -size 57235 +oid sha256:0227ce3fa0664ff4a99649532683837835733d1bb26dcd05b7f6277964b5115c +size 54342 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_5_en.png index b5f80f2faf..091b77a218 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8247dedc9811f2b325568973a2b714cd4b46322bcd0bf89af0e662518fa77747 -size 67694 +oid sha256:265d1fd77f393724a26a410e343b1dde4ce8e986db16ba00bb07def24aa52139 +size 68363 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_6_en.png index 0894037dc4..c301dfd550 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e389bce27c241cc13f60c247fea578fa4cd1ecc7448c9bf3ac3af1179e693216 -size 54701 +oid sha256:de11af4e521b9ea166dd422b94e1a6405753a8c00a9b637c76dd3b0e14774294 +size 55355 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_7_en.png index c4630c3e45..05b71c486e 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d56802b3e6dd21b20c4e92d47d9b880085f499feac855a46af601a5eb0bfc2f -size 54510 +oid sha256:6596062bc9bcbd81f51e1c7118426ec389c3bcae65df0793754c1a319399998f +size 55159 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_8_en.png index ce7b5ea699..85ef4f3937 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef1246daff44b29ee4c2b50538a3a948c2593749c70e15e053b8258a377c5756 -size 52745 +oid sha256:af598c1be693235acf29785c487fe69f0bdfc32408bddc948be78bc5fd8ce558 +size 53394 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_9_en.png index b6779aca93..f3460a5dd4 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0769e243dc60de52c16df23a86eb62167bb60cb756d113f88b9b476c025e402b -size 87117 +oid sha256:d2151f61b19e6d1710c9b9074abbcf2a927fca510cc6d3ef08746dcc4cf8e14a +size 87791 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_0_en.png index fe171e26b7..226f054223 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a7b2a6ccb43dc572140e2ce658967cd273d1122bf0216269a648920099a68d0 -size 62572 +oid sha256:a4c896b9025d301961b134c22ec1af858cec8a7b0eb1d823936f5af3cad38271 +size 63094 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_10_en.png index 420cb7c631..fc6b74a2f6 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d56c1af9fc73f4aaeed33dd8b6113ec64ddbfdf63506aa31ae6996732870baa2 -size 31127 +oid sha256:588859e742ca76818510abb9326969da5c227d0946ea6aac9de216e1a62a7d6a +size 31657 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_11_en.png index f36b5c2f83..6f2a17627d 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c3fb8ae530989e5a6d6e4193fbac4d4356af34076b684b782a0f8fec31226f1 -size 26657 +oid sha256:827036f5c685629037b5bb7ae3265b3261a52ab3d8b912344cd06137839512be +size 27266 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_13_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_13_en.png index 48b07fc9bd..d905171a03 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_13_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d302be6dbaef08b501f4f33d3e01338e068767702a7018bd5bb24ade6eee2cb -size 88558 +oid sha256:3691f9a0e8e90001011735c7496cc977995347eee2333d0f9bb37bd57b488fb8 +size 89113 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_14_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_14_en.png index 5cb7d766cd..a4ffe24031 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_14_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_14_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6be5cd35eec917bf1c96d03079df8235d0bc7b52159f54c8a177a64f648e58c2 -size 81212 +oid sha256:649be03a09cc9f34c0d59f035cdf26096b54b04d9dcb06f53882aae7a6c098cb +size 81789 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_15_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_15_en.png index 2691d1c1df..2e8b7314f6 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_15_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_15_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66d241a4270bef0a1f80c62c302cb5b40b4370f73e8175a746d32c90d1f98208 -size 49882 +oid sha256:422c10006716ea99ae5d3222334f4fe74a002e42fa2ab8b7890dff31a60ca8f7 +size 50444 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_16_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_16_en.png index 451ca0c3ec..cbe5cf415b 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_16_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_16_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1838a9839badf62d4d427a66a15a7d220996bdb0b60f0373249d62e4afae2910 -size 37390 +oid sha256:c748d1ae791d77ddfed18024a4ea04188adba1ac00aa43b5faa7660153cc9209 +size 34734 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_1_en.png index fe171e26b7..226f054223 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a7b2a6ccb43dc572140e2ce658967cd273d1122bf0216269a648920099a68d0 -size 62572 +oid sha256:a4c896b9025d301961b134c22ec1af858cec8a7b0eb1d823936f5af3cad38271 +size 63094 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_2_en.png index fe171e26b7..226f054223 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a7b2a6ccb43dc572140e2ce658967cd273d1122bf0216269a648920099a68d0 -size 62572 +oid sha256:a4c896b9025d301961b134c22ec1af858cec8a7b0eb1d823936f5af3cad38271 +size 63094 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_3_en.png index 89c4934dd9..b8280a1e69 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8172ca867a8c2e827ead36a22cefe967a5b57c69cae6d5aca1c941e4dd2ba16f -size 60160 +oid sha256:6329073993661030954c535849498a33dc3a95700ef3b1fae9f80cf0b5e6ffaa +size 60728 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_4_en.png index 390c9f6818..95f3da33c4 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13740b4fc4cbd24075606e45bf070a8fbb762d298857bc071399def91dda7175 -size 52189 +oid sha256:a04f8865034c36f32f0f7dd098fd6bf3e80f96869036f965c755a7a48286a071 +size 49232 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_5_en.png index fe171e26b7..226f054223 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a7b2a6ccb43dc572140e2ce658967cd273d1122bf0216269a648920099a68d0 -size 62572 +oid sha256:a4c896b9025d301961b134c22ec1af858cec8a7b0eb1d823936f5af3cad38271 +size 63094 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_6_en.png index 0950a6881e..75d8b98f01 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:842d5e0a5cc1102a2e320b43ea5ffda6cf63e94acdbf42adc39edaa1fb355812 -size 51742 +oid sha256:18d684eb1d4bbb7c3eb5de88a5123a60ff66ae625809761ae5f3624e443961c4 +size 52297 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_7_en.png index b29ccd3a11..0c77cb1a94 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82b75ce20a535cf1062c4b3ad86d08dc9044cf58d8035a5fdc68c48119157aae -size 51477 +oid sha256:6d4567aa39bf44dbf66c8c81f9195f0520364b1478b0bf9ef499923e0c267b9f +size 52015 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_8_en.png index 1b1e2edb98..f9b5ae643f 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bde8e9c5618ffc2b3019ce10db6fca44e4f23f3b987c222b7e838beb0dcb488 -size 49749 +oid sha256:729160a038512d10dbce06e6bdf8db15a2aca6b22624a096ade6687ca9c8b7de +size 50335 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_9_en.png index 1092dcd1b9..3ae5a4a153 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bce0bc8590037705369735e83e576f0818f4528298b4b2face999c8802f855b9 -size 81081 +oid sha256:76035b0f36179e93f4e35b9c59d51acee15e7db9d02249ee102656da88e04341 +size 81635 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_0_en.png index cf3ad87af2..1ba6334833 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5bf5bde86a7f7e873a6871d359f04f2139099724f9977601f5077b2590e97965 -size 46711 +oid sha256:a4e87a86ec5448c005cc236354c08668380023f3f734413edd38130af7ac9846 +size 46183 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_1_en.png index a31ad79f82..e165278f84 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:078079bf6b37471c7a16363c9d49f7deb5bd9d044ed0591ab86ac8a20800ba03 -size 46795 +oid sha256:9d7ecf7e48fc351e1936d9dcb7485873c93941248a5c63af8334fa4967bcca5b +size 46285 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_2_en.png index 5f77c0fa62..5b3e741c3f 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d36b97324b3794ff1d01fc13e9e59afb010130d6e022dcc2d7cc9387a13a787e -size 48387 +oid sha256:9db9d270801acdc43a87955bc055e5a25b9b1617de7e2aad93e4d390a36ac653 +size 47797 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_3_en.png index 5f67807828..8b83d0555c 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:986cfe49b91c522cf3a85d0425c0677a6929c44dca1531923bff2c1766ce46ae -size 59583 +oid sha256:373cfbe1fbd3d9e06bc2079b7ceadccce999efd0cbb1d43f7e791450f3882cad +size 59876 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_4_en.png index bc27628ac1..6febc55310 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f8ef2430abd11b31b3e90976f85bd4578e667bd0acc0851544ee4d16358479d -size 60237 +oid sha256:5192794371b4b3d10d5e8035f5ccf5dad66b04f16abb686cb5820d4647f678ef +size 60550 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_5_en.png index 490c357afb..b73534e92c 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7228db8e07c808d0a8780ed06f04f22ff8f1d620327f9243b1dedd476cecb902 -size 56896 +oid sha256:59c20c6ef29f76824f67bf26062586455a792bcbee79b7a0c648a1d69db08a65 +size 56400 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_0_en.png index 039776804a..463df3804a 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d28be1c00397b6ac456ef93dab7b1da469a7665380ab2c326124efeef3a1c150 -size 45143 +oid sha256:13cf913c2a26e30ad4c9700b110802a00213a514d3c9fcfbce81a82f7316f9fe +size 44515 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_1_en.png index 6847911e3c..3423750384 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:81523eeba6cf9e4ea1e92cc819845bc95bbd76f9e977b462f2357fe1d2e3d335 -size 45230 +oid sha256:5a20e35cfde7c96c6f4ceb5fe51af915f7a5ebdd16dc4dc4a0b4187b0163c42a +size 44611 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_2_en.png index 763fae759e..cb5e4713ba 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cdff40a4ce4b77a98818798c2b6ce3ad18ee5117f40c47c075b30d7006626f10 -size 46703 +oid sha256:28698a3c2fbbee2f76b6a3fb6312596102e7fc8743dc95db62816606b4109be0 +size 46054 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_3_en.png index 760d8a7533..8d9d32385a 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d331297c0912ffc278828cd00f886c926a71e59aadbee9fef994258ea56c822 -size 57757 +oid sha256:1ee744055473f7b652d874ff024ece68434e88ef085571dbab6466bcd3557c23 +size 58226 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_4_en.png index 058f822302..2c24a43118 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8265b9acc3932df6f0e1eee5e85c2b16d13060c4164c3fbb5b1b0b598745770c -size 58326 +oid sha256:e3c991f23daeb792f580afc18f3f528649cc92f27555ed05cc1abedad8accc27 +size 58781 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_5_en.png index 5f5b2315e6..abeece9d09 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:936cf07258d4a001b80b08df4b804c990b9e399bf8a5a544443beebf93caa298 -size 54825 +oid sha256:a95e67e4fd7d8886691d5df2571325254f49db24f8ab16335505b0c201cafb14 +size 54363 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en.png index 3caed99b5a..7230e781b2 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:53ec492b0a46d3add165e01c7ce540f304574791d10232a825e28cd15ae22248 -size 16903 +oid sha256:e582696ce8f1354353347853e8ad5e774b0c8fd2d34de5b1bc22460d372b2cf0 +size 16207 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en.png index c653ffd3fe..6f079aa16c 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:938b51a3e4e4466056da464fabd34066b716fbf55622b179f799181ff900819f -size 15978 +oid sha256:e34e3ba2f0c3bb8cbb0a21dea399dfa4ee34607c7ad4b2f86153d60f325696c3 +size 15232 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceHeaderView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceHeaderView_Day_0_en.png index f1bd04835c..341974f70a 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceHeaderView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceHeaderView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7853b79eda8a4b82cc49d553f78f545af62bc44b708a20a5bb5e4582ed07bc22 -size 63262 +oid sha256:72ea344a67f0c3cdb0037d63265414868bdc886ee0ef548ace738a7c95aa8ef6 +size 62034 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceHeaderView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceHeaderView_Night_0_en.png index 9eba778594..e813b4086b 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceHeaderView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceHeaderView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7daff199310c41900d0ca650f38ad2561c8db8542a4d48161a5a88f7fe976318 -size 61899 +oid sha256:0af67c43f3a8b3a5b322a9ab46a46468aa45df57e181e661b9a8c4489d737aff +size 61205 From 64f1f299e6b361e010b66b092e8a49581354b0d5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 09:50:04 +0200 Subject: [PATCH 012/112] `LoggedInEvents` -> `LoggedInEvent` --- .../loggedin/{LoggedInEvents.kt => LoggedInEvent.kt} | 12 ++++++------ .../android/appnav/loggedin/LoggedInPresenter.kt | 12 ++++++------ .../element/android/appnav/loggedin/LoggedInState.kt | 2 +- .../element/android/appnav/loggedin/LoggedInView.kt | 12 ++++++------ .../android/appnav/loggedin/LoggedInPresenterTest.kt | 8 ++++---- 5 files changed, 23 insertions(+), 23 deletions(-) rename appnav/src/main/kotlin/io/element/android/appnav/loggedin/{LoggedInEvents.kt => LoggedInEvent.kt} (90%) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInEvents.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInEvent.kt similarity index 90% rename from appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInEvents.kt rename to appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInEvent.kt index 66fffce9e8..34ba2e6460 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInEvents.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInEvent.kt @@ -8,11 +8,11 @@ package io.element.android.appnav.loggedin -sealed interface LoggedInEvents { - data class CloseErrorDialog(val doNotShowAgain: Boolean) : LoggedInEvents - data object CheckSlidingSyncProxyAvailability : LoggedInEvents - data object LogoutAndMigrateToNativeSlidingSync : LoggedInEvents - data object DismissLocalNetworkPermissionPrompt : LoggedInEvents +sealed interface LoggedInEvent { + data class CloseErrorDialog(val doNotShowAgain: Boolean) : LoggedInEvent + data object CheckSlidingSyncProxyAvailability : LoggedInEvent + data object LogoutAndMigrateToNativeSlidingSync : LoggedInEvent + data object DismissLocalNetworkPermissionPrompt : LoggedInEvent - data object RequestLocationNetworkPermission : LoggedInEvents + data object RequestLocationNetworkPermission : LoggedInEvent } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt index 5cd3efa801..f6faee0acf 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt @@ -149,9 +149,9 @@ class LoggedInPresenter( else -> LocalNetworkPermissionDialog.Settings } - fun handleEvent(event: LoggedInEvents) { + fun handleEvent(event: LoggedInEvent) { when (event) { - is LoggedInEvents.CloseErrorDialog -> { + is LoggedInEvent.CloseErrorDialog -> { pusherRegistrationState.value = AsyncData.Uninitialized if (event.doNotShowAgain) { coroutineScope.launch { @@ -159,17 +159,17 @@ class LoggedInPresenter( } } } - LoggedInEvents.CheckSlidingSyncProxyAvailability -> coroutineScope.launch { + LoggedInEvent.CheckSlidingSyncProxyAvailability -> coroutineScope.launch { forceNativeSlidingSyncMigration = matrixClient.needsForcedNativeSlidingSyncMigration().getOrDefault(false) } - LoggedInEvents.LogoutAndMigrateToNativeSlidingSync -> coroutineScope.launch { + LoggedInEvent.LogoutAndMigrateToNativeSlidingSync -> coroutineScope.launch { // Force the logout since Native Sliding Sync is already enforced by the SDK matrixClient.logout(userInitiated = true, ignoreSdkError = true) } - LoggedInEvents.DismissLocalNetworkPermissionPrompt -> { + LoggedInEvent.DismissLocalNetworkPermissionPrompt -> { localNetworkPromptDismissedThisSession = true } - LoggedInEvents.RequestLocationNetworkPermission -> { + LoggedInEvent.RequestLocationNetworkPermission -> { if (localNetworkPermissionDialog == LocalNetworkPermissionDialog.Settings) { localNetworkPermissionState.eventSink(PermissionsEvent.OpenSystemSettingAndCloseDialog) } else { diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt index edc12383bf..33499341e7 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt @@ -18,5 +18,5 @@ data class LoggedInState( val forceNativeSlidingSyncMigration: Boolean, val appName: String, val localNetworkPermissionDialog: LocalNetworkPermissionDialog, - val eventSink: (LoggedInEvents) -> Unit, + val eventSink: (LoggedInEvent) -> Unit, ) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt index 7e607e7d92..060a24855a 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt @@ -37,7 +37,7 @@ fun LoggedInView( ) { OnLifecycleEvent { _, event -> if (event == Lifecycle.Event.ON_RESUME) { - state.eventSink(LoggedInEvents.CheckSlidingSyncProxyAvailability) + state.eventSink(LoggedInEvent.CheckSlidingSyncProxyAvailability) } } Box( @@ -63,10 +63,10 @@ fun LoggedInView( content = stringResource(id = CommonStrings.common_error_registering_pusher_android, reason), cancelText = stringResource(id = CommonStrings.common_settings), onDismiss = { - state.eventSink(LoggedInEvents.CloseErrorDialog(it)) + state.eventSink(LoggedInEvent.CloseErrorDialog(it)) }, onCancel = { - state.eventSink(LoggedInEvents.CloseErrorDialog(false)) + state.eventSink(LoggedInEvent.CloseErrorDialog(false)) navigateToNotificationTroubleshoot() } ) @@ -79,15 +79,15 @@ fun LoggedInView( ForceNativeSlidingSyncMigrationDialog( appName = state.appName, onSubmit = { - state.eventSink(LoggedInEvents.LogoutAndMigrateToNativeSlidingSync) + state.eventSink(LoggedInEvent.LogoutAndMigrateToNativeSlidingSync) } ) } LocalNetworkPermissionDialogView( dialog = state.localNetworkPermissionDialog, - onSubmit = { state.eventSink(LoggedInEvents.RequestLocationNetworkPermission) }, - onDismiss = { state.eventSink(LoggedInEvents.DismissLocalNetworkPermissionPrompt) }, + onSubmit = { state.eventSink(LoggedInEvent.RequestLocationNetworkPermission) }, + onDismiss = { state.eventSink(LoggedInEvent.DismissLocalNetworkPermissionPrompt) }, ) } 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 0e2d459d1e..f85d35bbb5 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 @@ -210,7 +210,7 @@ class LoggedInPresenterTest { lambda.assertions() .isCalledOnce() // Reset the error and do not show again - finalState.eventSink(LoggedInEvents.CloseErrorDialog(doNotShowAgain = false)) + finalState.eventSink(LoggedInEvent.CloseErrorDialog(doNotShowAgain = false)) val lastState = awaitItem() assertThat(lastState.pusherRegistrationState.isUninitialized()).isTrue() assertThat(lastState.ignoreRegistrationError).isFalse() @@ -240,7 +240,7 @@ class LoggedInPresenterTest { lambda.assertions() .isCalledOnce() // Reset the error and do not show again - finalState.eventSink(LoggedInEvents.CloseErrorDialog(doNotShowAgain = true)) + finalState.eventSink(LoggedInEvent.CloseErrorDialog(doNotShowAgain = true)) skipItems(1) setIgnoreRegistrationErrorLambda.assertions() .isCalledOnce() @@ -296,7 +296,7 @@ class LoggedInPresenterTest { ).test { val initialState = awaitItem() assertThat(initialState.forceNativeSlidingSyncMigration).isFalse() - initialState.eventSink(LoggedInEvents.CheckSlidingSyncProxyAvailability) + initialState.eventSink(LoggedInEvent.CheckSlidingSyncProxyAvailability) assertThat(awaitItem().forceNativeSlidingSyncMigration).isTrue() } } @@ -318,7 +318,7 @@ class LoggedInPresenterTest { ).test { val initialState = awaitItem() - initialState.eventSink(LoggedInEvents.LogoutAndMigrateToNativeSlidingSync) + initialState.eventSink(LoggedInEvent.LogoutAndMigrateToNativeSlidingSync) advanceUntilIdle() From 7353e0e476afaf0ae7f0ce12b1ec343a07378b50 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 09:50:39 +0200 Subject: [PATCH 013/112] `AnalyticsOptInEvents` -> `AnalyticsOptInEvent` --- .../api/{AnalyticsOptInEvents.kt => AnalyticsOptInEvent.kt} | 4 ++-- .../analytics/api/preferences/AnalyticsPreferencesState.kt | 4 ++-- .../analytics/api/preferences/AnalyticsPreferencesView.kt | 4 ++-- .../features/analytics/impl/AnalyticsOptInPresenter.kt | 6 +++--- .../android/features/analytics/impl/AnalyticsOptInState.kt | 4 ++-- .../android/features/analytics/impl/AnalyticsOptInView.kt | 6 +++--- .../impl/preferences/AnalyticsPreferencesPresenter.kt | 6 +++--- .../features/analytics/impl/AnalyticsOptInPresenterTest.kt | 6 +++--- .../impl/preferences/AnalyticsPreferencesPresenterTest.kt | 6 +++--- 9 files changed, 23 insertions(+), 23 deletions(-) rename features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/{AnalyticsOptInEvents.kt => AnalyticsOptInEvent.kt} (86%) diff --git a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/AnalyticsOptInEvents.kt b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/AnalyticsOptInEvent.kt similarity index 86% rename from features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/AnalyticsOptInEvents.kt rename to features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/AnalyticsOptInEvent.kt index 4181f52ba8..6095bc67da 100644 --- a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/AnalyticsOptInEvents.kt +++ b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/AnalyticsOptInEvent.kt @@ -8,6 +8,6 @@ package io.element.android.features.analytics.api -sealed interface AnalyticsOptInEvents { - data class EnableAnalytics(val isEnabled: Boolean) : AnalyticsOptInEvents +sealed interface AnalyticsOptInEvent { + data class EnableAnalytics(val isEnabled: Boolean) : AnalyticsOptInEvent } diff --git a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesState.kt b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesState.kt index 20a180446c..b5f049460d 100644 --- a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesState.kt +++ b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesState.kt @@ -8,11 +8,11 @@ package io.element.android.features.analytics.api.preferences -import io.element.android.features.analytics.api.AnalyticsOptInEvents +import io.element.android.features.analytics.api.AnalyticsOptInEvent data class AnalyticsPreferencesState( val applicationName: String, val isEnabled: Boolean, val policyUrl: String, - val eventSink: (AnalyticsOptInEvents) -> Unit, + val eventSink: (AnalyticsOptInEvent) -> Unit, ) diff --git a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt index 0d141e5ed2..31891a0162 100644 --- a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt +++ b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt @@ -13,7 +13,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter -import io.element.android.features.analytics.api.AnalyticsOptInEvents +import io.element.android.features.analytics.api.AnalyticsOptInEvent import io.element.android.features.analytics.api.R import io.element.android.libraries.designsystem.components.LINK_TAG import io.element.android.libraries.designsystem.components.list.ListItemContent @@ -30,7 +30,7 @@ fun AnalyticsPreferencesView( modifier: Modifier = Modifier, ) { fun onEnabledChanged(isEnabled: Boolean) { - state.eventSink(AnalyticsOptInEvents.EnableAnalytics(isEnabled = isEnabled)) + state.eventSink(AnalyticsOptInEvent.EnableAnalytics(isEnabled = isEnabled)) } val supportingText = stringResource( diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenter.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenter.kt index d7bdb85e35..05d2c2fefd 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenter.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenter.kt @@ -12,7 +12,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import dev.zacsweers.metro.Inject import io.element.android.appconfig.AnalyticsConfig -import io.element.android.features.analytics.api.AnalyticsOptInEvents +import io.element.android.features.analytics.api.AnalyticsOptInEvent import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.meta.BuildMeta import io.element.android.services.analytics.api.AnalyticsService @@ -28,9 +28,9 @@ class AnalyticsOptInPresenter( override fun present(): AnalyticsOptInState { val localCoroutineScope = rememberCoroutineScope() - fun handleEvent(event: AnalyticsOptInEvents) { + fun handleEvent(event: AnalyticsOptInEvent) { when (event) { - is AnalyticsOptInEvents.EnableAnalytics -> localCoroutineScope.setIsEnabled(event.isEnabled) + is AnalyticsOptInEvent.EnableAnalytics -> localCoroutineScope.setIsEnabled(event.isEnabled) } localCoroutineScope.launch { analyticsService.setDidAskUserConsent() diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInState.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInState.kt index b2ebd37e20..063ed316c8 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInState.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInState.kt @@ -8,10 +8,10 @@ package io.element.android.features.analytics.impl -import io.element.android.features.analytics.api.AnalyticsOptInEvents +import io.element.android.features.analytics.api.AnalyticsOptInEvent data class AnalyticsOptInState( val applicationName: String, val hasPolicyLink: Boolean, - val eventSink: (AnalyticsOptInEvents) -> Unit + val eventSink: (AnalyticsOptInEvent) -> Unit ) diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt index 678d28c5c9..3ead26ea48 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt @@ -28,7 +28,7 @@ import androidx.compose.ui.unit.dp import io.element.android.appconfig.AnalyticsConfig import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.features.analytics.api.AnalyticsOptInEvents +import io.element.android.features.analytics.api.AnalyticsOptInEvent import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage @@ -55,11 +55,11 @@ fun AnalyticsOptInView( val eventSink = state.eventSink fun onAcceptTerms() { - eventSink(AnalyticsOptInEvents.EnableAnalytics(true)) + eventSink(AnalyticsOptInEvent.EnableAnalytics(true)) } fun onDeclineTerms() { - eventSink(AnalyticsOptInEvents.EnableAnalytics(false)) + eventSink(AnalyticsOptInEvent.EnableAnalytics(false)) } BackHandler(onBack = ::onDeclineTerms) diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenter.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenter.kt index 110cb98d41..ca3388ee3c 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenter.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenter.kt @@ -13,7 +13,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.rememberCoroutineScope import dev.zacsweers.metro.Inject import io.element.android.appconfig.AnalyticsConfig -import io.element.android.features.analytics.api.AnalyticsOptInEvents +import io.element.android.features.analytics.api.AnalyticsOptInEvent import io.element.android.features.analytics.api.preferences.AnalyticsPreferencesState import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.meta.BuildMeta @@ -31,9 +31,9 @@ class AnalyticsPreferencesPresenter( val localCoroutineScope = rememberCoroutineScope() val isEnabled = analyticsService.userConsentFlow.collectAsState(initial = false) - fun handleEvent(event: AnalyticsOptInEvents) { + fun handleEvent(event: AnalyticsOptInEvent) { when (event) { - is AnalyticsOptInEvents.EnableAnalytics -> localCoroutineScope.setIsEnabled(event.isEnabled) + is AnalyticsOptInEvent.EnableAnalytics -> localCoroutineScope.setIsEnabled(event.isEnabled) } } diff --git a/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt b/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt index 6521c7f597..f2093dfa72 100644 --- a/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt +++ b/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt @@ -12,7 +12,7 @@ import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.features.analytics.api.AnalyticsOptInEvents +import io.element.android.features.analytics.api.AnalyticsOptInEvent import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.WarmUpRule @@ -37,7 +37,7 @@ class AnalyticsOptInPresenterTest { }.test { val initialState = awaitItem() assertThat(analyticsService.didAskUserConsentFlow.first()).isFalse() - initialState.eventSink.invoke(AnalyticsOptInEvents.EnableAnalytics(true)) + initialState.eventSink.invoke(AnalyticsOptInEvent.EnableAnalytics(true)) assertThat(analyticsService.didAskUserConsentFlow.first()).isTrue() assertThat(analyticsService.userConsentFlow.first()).isTrue() } @@ -55,7 +55,7 @@ class AnalyticsOptInPresenterTest { }.test { val initialState = awaitItem() assertThat(analyticsService.didAskUserConsentFlow.first()).isFalse() - initialState.eventSink.invoke(AnalyticsOptInEvents.EnableAnalytics(false)) + initialState.eventSink.invoke(AnalyticsOptInEvent.EnableAnalytics(false)) assertThat(analyticsService.didAskUserConsentFlow.first()).isTrue() assertThat(analyticsService.userConsentFlow.first()).isFalse() } diff --git a/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt b/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt index 174935a1ad..29f7d9af03 100644 --- a/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt +++ b/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt @@ -13,7 +13,7 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.AnalyticsConfig -import io.element.android.features.analytics.api.AnalyticsOptInEvents +import io.element.android.features.analytics.api.AnalyticsOptInEvent import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.WarmUpRule @@ -67,9 +67,9 @@ class AnalyticsPreferencesPresenterTest { skipItems(1) val initialState = awaitItem() assertThat(initialState.isEnabled).isTrue() - initialState.eventSink.invoke(AnalyticsOptInEvents.EnableAnalytics(false)) + initialState.eventSink.invoke(AnalyticsOptInEvent.EnableAnalytics(false)) assertThat(awaitItem().isEnabled).isFalse() - initialState.eventSink.invoke(AnalyticsOptInEvents.EnableAnalytics(true)) + initialState.eventSink.invoke(AnalyticsOptInEvent.EnableAnalytics(true)) assertThat(awaitItem().isEnabled).isTrue() } } From dff9af9ce0fc343c4e7bb29e557addd0e34af8de Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 09:51:17 +0200 Subject: [PATCH 014/112] `ConfigureRoomEvents` -> `ConfigureRoomEvent` --- ...ureRoomEvents.kt => ConfigureRoomEvent.kt} | 18 ++--- .../configureroom/ConfigureRoomPresenter.kt | 18 ++--- .../impl/configureroom/ConfigureRoomState.kt | 2 +- .../ConfigureRoomStatePreviewParam.kt | 2 +- .../impl/configureroom/ConfigureRoomView.kt | 18 ++--- .../impl/ConfigureRoomPresenterTest.kt | 72 +++++++++---------- 6 files changed, 65 insertions(+), 65 deletions(-) rename features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/{ConfigureRoomEvents.kt => ConfigureRoomEvent.kt} (78%) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvent.kt similarity index 78% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt rename to features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvent.kt index 5de99e519d..e22e08a5b0 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvent.kt @@ -11,13 +11,13 @@ package io.element.android.features.createroom.impl.configureroom import io.element.android.libraries.matrix.api.spaces.SpaceRoom import io.element.android.libraries.matrix.ui.media.AvatarAction -sealed interface ConfigureRoomEvents { - data class RoomNameChanged(val name: String) : ConfigureRoomEvents - data class TopicChanged(val topic: String) : ConfigureRoomEvents - data class JoinRuleChanged(val joinRuleItem: JoinRuleItem) : ConfigureRoomEvents - data class RoomAddressChanged(val roomAddress: String) : ConfigureRoomEvents - data object CreateRoom : ConfigureRoomEvents - data class HandleAvatarAction(val action: AvatarAction) : ConfigureRoomEvents - data class SetParentSpace(val space: SpaceRoom?) : ConfigureRoomEvents - data object CancelCreateRoom : ConfigureRoomEvents +sealed interface ConfigureRoomEvent { + data class RoomNameChanged(val name: String) : ConfigureRoomEvent + data class TopicChanged(val topic: String) : ConfigureRoomEvent + data class JoinRuleChanged(val joinRuleItem: JoinRuleItem) : ConfigureRoomEvent + data class RoomAddressChanged(val roomAddress: String) : ConfigureRoomEvent + data object CreateRoom : ConfigureRoomEvent + data class HandleAvatarAction(val action: AvatarAction) : ConfigureRoomEvent + data class SetParentSpace(val space: SpaceRoom?) : ConfigureRoomEvent + data object CancelCreateRoom : ConfigureRoomEvent } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index abedc2bd49..849647a9d3 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -188,14 +188,14 @@ class ConfigureRoomPresenter( localCoroutineScope.createRoom(config, createRoomAction) } - fun handleEvent(event: ConfigureRoomEvents) { + fun handleEvent(event: ConfigureRoomEvent) { when (event) { - is ConfigureRoomEvents.RoomNameChanged -> dataStore.setRoomName(event.name) - is ConfigureRoomEvents.TopicChanged -> dataStore.setTopic(event.topic) - is ConfigureRoomEvents.JoinRuleChanged -> dataStore.setJoinRule(event.joinRuleItem) - is ConfigureRoomEvents.RoomAddressChanged -> dataStore.setRoomAddress(event.roomAddress) - is ConfigureRoomEvents.CreateRoom -> createRoom(createRoomConfig) - is ConfigureRoomEvents.HandleAvatarAction -> { + is ConfigureRoomEvent.RoomNameChanged -> dataStore.setRoomName(event.name) + is ConfigureRoomEvent.TopicChanged -> dataStore.setTopic(event.topic) + is ConfigureRoomEvent.JoinRuleChanged -> dataStore.setJoinRule(event.joinRuleItem) + is ConfigureRoomEvent.RoomAddressChanged -> dataStore.setRoomAddress(event.roomAddress) + is ConfigureRoomEvent.CreateRoom -> createRoom(createRoomConfig) + is ConfigureRoomEvent.HandleAvatarAction -> { when (event.action) { AvatarAction.ChoosePhoto -> galleryImagePicker.launch() AvatarAction.TakePhoto -> if (cameraPermissionState.permissionGranted) { @@ -207,10 +207,10 @@ class ConfigureRoomPresenter( AvatarAction.Remove -> dataStore.setAvatarUri(uri = null) } } - is ConfigureRoomEvents.SetParentSpace -> { + is ConfigureRoomEvent.SetParentSpace -> { dataStore.setParentSpace(event.space, false) } - ConfigureRoomEvents.CancelCreateRoom -> { + ConfigureRoomEvent.CancelCreateRoom -> { createRoomAction.value = AsyncAction.Uninitialized } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt index 0da931fa51..15f549d28f 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt @@ -26,7 +26,7 @@ data class ConfigureRoomState( val homeserverName: String, val availableJoinRules: ImmutableList, val spaces: ImmutableList, - val eventSink: (ConfigureRoomEvents) -> Unit + val eventSink: (ConfigureRoomEvent) -> Unit ) { val isValid: Boolean = config.roomName?.isNotEmpty() == true && (config.visibilityState is RoomVisibilityState.Private || roomAddressValidity == RoomAddressValidity.Valid) && diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStatePreviewParam.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStatePreviewParam.kt index 151c7d13d0..725cfc2309 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStatePreviewParam.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStatePreviewParam.kt @@ -143,7 +143,7 @@ fun aConfigureRoomState( ) }, spaces: List = emptyList(), - eventSink: (ConfigureRoomEvents) -> Unit = { }, + eventSink: (ConfigureRoomEvent) -> Unit = { }, ) = ConfigureRoomState( config = config, isSpace = isSpace, 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 e39df69f97..fec820ffbb 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 @@ -96,7 +96,7 @@ fun ConfigureRoomView( onBackClick = onBackClick, onNextClick = { focusManager.clearFocus() - state.eventSink(ConfigureRoomEvents.CreateRoom) + state.eventSink(ConfigureRoomEvent.CreateRoom) }, ) } @@ -114,20 +114,20 @@ fun ConfigureRoomView( avatarUri = state.config.avatarUri, roomName = state.config.roomName.orEmpty(), onAvatarClick = ::onAvatarClick, - onChangeRoomName = { state.eventSink(ConfigureRoomEvents.RoomNameChanged(it)) }, + onChangeRoomName = { state.eventSink(ConfigureRoomEvent.RoomNameChanged(it)) }, ) Spacer(modifier = Modifier.height(16.dp)) RoomTopic( modifier = Modifier.padding(horizontal = 16.dp), topic = state.config.topic.orEmpty(), - onTopicChange = { state.eventSink(ConfigureRoomEvents.TopicChanged(it)) }, + onTopicChange = { state.eventSink(ConfigureRoomEvent.TopicChanged(it)) }, ) Spacer(modifier = Modifier.height(16.dp)) if (!state.isSpace && state.spaces.isNotEmpty()) { SelectParentSpaceOptions( spaces = state.spaces, selectedSpace = state.config.parentSpace, - onSelectSpace = { state.eventSink(ConfigureRoomEvents.SetParentSpace(it)) }, + onSelectSpace = { state.eventSink(ConfigureRoomEvent.SetParentSpace(it)) }, ) } RoomJoinRuleOptions( @@ -136,7 +136,7 @@ fun ConfigureRoomView( parentSpace = state.config.parentSpace, onOptionClick = { focusManager.clearFocus() - state.eventSink(ConfigureRoomEvents.JoinRuleChanged(it)) + state.eventSink(ConfigureRoomEvent.JoinRuleChanged(it)) }, ) if (state.config.visibilityState !is RoomVisibilityState.Private) { @@ -146,7 +146,7 @@ fun ConfigureRoomView( address = state.config.visibilityState.roomAddress().getOrNull().orEmpty(), homeserverName = state.homeserverName, addressValidity = state.roomAddressValidity, - onAddressChange = { state.eventSink(ConfigureRoomEvents.RoomAddressChanged(it)) }, + onAddressChange = { state.eventSink(ConfigureRoomEvent.RoomAddressChanged(it)) }, label = null, supportingText = stringResource(R.string.screen_create_room_room_address_section_footer), ) @@ -158,7 +158,7 @@ fun ConfigureRoomView( actions = state.avatarActions, isVisible = isAvatarActionsSheetVisible.value, onDismiss = { isAvatarActionsSheetVisible.value = false }, - onSelectAction = { state.eventSink(ConfigureRoomEvents.HandleAvatarAction(it)) } + onSelectAction = { state.eventSink(ConfigureRoomEvent.HandleAvatarAction(it)) } ) AsyncActionView( @@ -170,8 +170,8 @@ fun ConfigureRoomView( }, onSuccess = { onCreateRoomSuccess(it) }, errorMessage = { stringResource(if (isSpace) R.string.screen_create_room_error_creating_space else R.string.screen_create_room_error_creating_room) }, - onRetry = { state.eventSink(ConfigureRoomEvents.CreateRoom) }, - onErrorDismiss = { state.eventSink(ConfigureRoomEvents.CancelCreateRoom) }, + onRetry = { state.eventSink(ConfigureRoomEvent.CreateRoom) }, + onErrorDismiss = { state.eventSink(ConfigureRoomEvent.CancelCreateRoom) }, ) PermissionsView( diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/ConfigureRoomPresenterTest.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/ConfigureRoomPresenterTest.kt index 046d00a5c6..09afd38f90 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/ConfigureRoomPresenterTest.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/ConfigureRoomPresenterTest.kt @@ -12,7 +12,7 @@ import androidx.core.net.toUri import app.cash.turbine.TurbineTestContext import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.CreatedRoom -import io.element.android.features.createroom.impl.configureroom.ConfigureRoomEvents +import io.element.android.features.createroom.impl.configureroom.ConfigureRoomEvent import io.element.android.features.createroom.impl.configureroom.ConfigureRoomPresenter import io.element.android.features.createroom.impl.configureroom.ConfigureRoomState import io.element.android.features.createroom.impl.configureroom.CreateRoomConfig @@ -106,14 +106,14 @@ class ConfigureRoomPresenterTest : RobolectricTest() { assertThat(initialState.isValid).isFalse() // Room name not empty - initialState.eventSink(ConfigureRoomEvents.RoomNameChanged(A_ROOM_NAME)) + initialState.eventSink(ConfigureRoomEvent.RoomNameChanged(A_ROOM_NAME)) var newState: ConfigureRoomState = awaitItem() config = config.copy(roomName = A_ROOM_NAME) assertThat(newState.config).isEqualTo(config) assertThat(newState.isValid).isTrue() // Clear room name - newState.eventSink(ConfigureRoomEvents.RoomNameChanged("")) + newState.eventSink(ConfigureRoomEvent.RoomNameChanged("")) newState = awaitItem() config = config.copy(roomName = null) assertThat(newState.config).isEqualTo(config) @@ -136,13 +136,13 @@ class ConfigureRoomPresenterTest : RobolectricTest() { var expectedConfig = CreateRoomConfig() assertThat(initialState.config).isEqualTo(expectedConfig) // Room name - initialState.eventSink(ConfigureRoomEvents.RoomNameChanged(A_ROOM_NAME)) + initialState.eventSink(ConfigureRoomEvent.RoomNameChanged(A_ROOM_NAME)) var newState = awaitItem() expectedConfig = expectedConfig.copy(roomName = A_ROOM_NAME) assertThat(newState.config).isEqualTo(expectedConfig) // Room topic - newState.eventSink(ConfigureRoomEvents.TopicChanged(A_MESSAGE)) + newState.eventSink(ConfigureRoomEvent.TopicChanged(A_MESSAGE)) newState = awaitItem() expectedConfig = expectedConfig.copy(topic = A_MESSAGE) assertThat(newState.config).isEqualTo(expectedConfig) @@ -153,7 +153,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() { // From gallery val uriFromGallery = AN_URI_FROM_GALLERY pickerProvider.givenResult(uriFromGallery.toUri()) - newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) + newState.eventSink(ConfigureRoomEvent.HandleAvatarAction(AvatarAction.ChoosePhoto)) newState = awaitItem() expectedConfig = expectedConfig.copy(avatarUri = uriFromGallery) assertThat(newState.config).isEqualTo(expectedConfig) @@ -161,7 +161,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() { val uriFromCamera = AN_URI_FROM_CAMERA pickerProvider.givenResult(uriFromCamera.toUri()) assertThat(newState.cameraPermissionState.permissionGranted).isFalse() - newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto)) + newState.eventSink(ConfigureRoomEvent.HandleAvatarAction(AvatarAction.TakePhoto)) newState = awaitItem() assertThat(newState.cameraPermissionState.showDialog).isTrue() permissionsPresenter.setPermissionGranted() @@ -173,18 +173,18 @@ class ConfigureRoomPresenterTest : RobolectricTest() { // Do it again, no permission is requested val uriFromCamera2 = AN_URI_FROM_CAMERA_2 pickerProvider.givenResult(uriFromCamera2.toUri()) - newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto)) + newState.eventSink(ConfigureRoomEvent.HandleAvatarAction(AvatarAction.TakePhoto)) newState = awaitItem() expectedConfig = expectedConfig.copy(avatarUri = uriFromCamera2) assertThat(newState.config).isEqualTo(expectedConfig) // Remove - newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.Remove)) + newState.eventSink(ConfigureRoomEvent.HandleAvatarAction(AvatarAction.Remove)) newState = awaitItem() expectedConfig = expectedConfig.copy(avatarUri = null) assertThat(newState.config).isEqualTo(expectedConfig) // Room privacy - newState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public)) + newState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public)) newState = awaitItem() expectedConfig = expectedConfig.copy( visibilityState = RoomVisibilityState.Public( @@ -208,7 +208,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() { matrixClient.givenCreateRoomResult(createRoomResult) - initialState.eventSink(ConfigureRoomEvents.CreateRoom) + initialState.eventSink(ConfigureRoomEvent.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterCreateRoom = awaitItem() assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Success::class.java) @@ -239,13 +239,13 @@ class ConfigureRoomPresenterTest : RobolectricTest() { // Use a public parent space so AskToJoin is a valid option val parentSpace = aSpaceRoom(joinRule = JoinRule.Public) - initialState.eventSink(ConfigureRoomEvents.SetParentSpace(parentSpace)) + initialState.eventSink(ConfigureRoomEvent.SetParentSpace(parentSpace)) assertThat(awaitItem().config.parentSpace).isEqualTo(parentSpace) - initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.AskToJoin)) + initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.AskToJoin)) assertThat(awaitItem().config.visibilityState.joinRuleItem).isEqualTo(JoinRuleItem.PublicVisibility.AskToJoin) - initialState.eventSink(ConfigureRoomEvents.CreateRoom) + initialState.eventSink(ConfigureRoomEvent.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterCreateRoom = awaitItem() @@ -282,13 +282,13 @@ class ConfigureRoomPresenterTest : RobolectricTest() { // Use a public parent space so AskToJoin is a valid option val parentSpace = aSpaceRoom(joinRule = JoinRule.Public) - initialState.eventSink(ConfigureRoomEvents.SetParentSpace(parentSpace)) + initialState.eventSink(ConfigureRoomEvent.SetParentSpace(parentSpace)) assertThat(awaitItem().config.parentSpace).isEqualTo(parentSpace) - initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.AskToJoin)) + initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.AskToJoin)) assertThat(awaitItem().config.visibilityState.joinRuleItem).isEqualTo(JoinRuleItem.PublicVisibility.AskToJoin) - initialState.eventSink(ConfigureRoomEvents.CreateRoom) + initialState.eventSink(ConfigureRoomEvent.CreateRoom) // We immediately receive the room power levels info needed for adding the child to a space val powerLevels = RoomPowerLevels( @@ -339,7 +339,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() { matrixClient.givenCreateRoomResult(createRoomResult) - initialState.eventSink(ConfigureRoomEvents.CreateRoom) + initialState.eventSink(ConfigureRoomEvent.CreateRoom) skipItems(2) val analyticsEvent = analyticsService.capturedEvents.filterIsInstance().firstOrNull() @@ -369,14 +369,14 @@ class ConfigureRoomPresenterTest : RobolectricTest() { mediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(file, mockk(), mockk()))) matrixClient.givenUploadMediaResult(Result.failure(AN_EXCEPTION)) - initialState.eventSink(ConfigureRoomEvents.CreateRoom) + initialState.eventSink(ConfigureRoomEvent.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterCreateRoom = awaitItem() assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java) assertThat(analyticsService.capturedEvents.filterIsInstance()).isEmpty() matrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL)) - stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom) + stateAfterCreateRoom.eventSink(ConfigureRoomEvent.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Success::class.java) @@ -399,14 +399,14 @@ class ConfigureRoomPresenterTest : RobolectricTest() { fakeMatrixClient.givenCreateRoomResult(createRoomResult) // Create - initialState.eventSink(ConfigureRoomEvents.CreateRoom) + initialState.eventSink(ConfigureRoomEvent.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterCreateRoom = awaitItem() assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java) assertThat((stateAfterCreateRoom.createRoomAction as? AsyncAction.Failure)?.error).isEqualTo(createRoomResult.exceptionOrNull()) // Retry - stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom) + stateAfterCreateRoom.eventSink(ConfigureRoomEvent.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterRetry = awaitItem() @@ -414,7 +414,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() { assertThat((stateAfterRetry.createRoomAction as? AsyncAction.Failure)?.error).isEqualTo(createRoomResult.exceptionOrNull()) // Cancel - stateAfterRetry.eventSink(ConfigureRoomEvents.CancelCreateRoom) + stateAfterRetry.eventSink(ConfigureRoomEvent.CancelCreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) } } @@ -430,9 +430,9 @@ class ConfigureRoomPresenterTest : RobolectricTest() { ) presenter.test { val initialState = initialState() - initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public)) + initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public)) skipItems(1) - initialState.eventSink(ConfigureRoomEvents.RoomAddressChanged("invalid address")) + initialState.eventSink(ConfigureRoomEvent.RoomAddressChanged("invalid address")) skipItems(1) advanceUntilIdle() awaitItem().also { state -> @@ -450,9 +450,9 @@ class ConfigureRoomPresenterTest : RobolectricTest() { ) presenter.test { val initialState = initialState() - initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public)) + initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public)) skipItems(1) - initialState.eventSink(ConfigureRoomEvents.RoomAddressChanged("address")) + initialState.eventSink(ConfigureRoomEvent.RoomAddressChanged("address")) skipItems(1) advanceUntilIdle() awaitItem().also { state -> @@ -470,9 +470,9 @@ class ConfigureRoomPresenterTest : RobolectricTest() { ) presenter.test { val initialState = initialState() - initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public)) + initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public)) skipItems(1) - initialState.eventSink(ConfigureRoomEvents.RoomAddressChanged("address")) + initialState.eventSink(ConfigureRoomEvent.RoomAddressChanged("address")) skipItems(1) advanceUntilIdle() awaitItem().also { state -> @@ -488,22 +488,22 @@ class ConfigureRoomPresenterTest : RobolectricTest() { val initialState = initialState() // First change the join rule to public - initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public)) + initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public)) assertThat(awaitItem().config.visibilityState).isInstanceOf(RoomVisibilityState.Public::class.java) // Then check changing the parent space resets it to private // (via LaunchedEffect fallback since Public is not in availableJoinRules for non-public parent) - initialState.eventSink(ConfigureRoomEvents.SetParentSpace(aSpaceRoom())) + initialState.eventSink(ConfigureRoomEvent.SetParentSpace(aSpaceRoom())) skipItems(1) // Skip intermediate state assertThat(awaitItem().config.visibilityState).isEqualTo(RoomVisibilityState.Private(JoinRuleItem.PrivateVisibility.Private)) // If we change the join rule back to public - initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public)) + initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public)) skipItems(1) // Skip intermediate state (Public is still invalid) assertThat(awaitItem().config.visibilityState).isEqualTo(RoomVisibilityState.Private(JoinRuleItem.PrivateVisibility.Private)) // Then remove the parent space, the join rule stays private - initialState.eventSink(ConfigureRoomEvents.SetParentSpace(null)) + initialState.eventSink(ConfigureRoomEvent.SetParentSpace(null)) assertThat(awaitItem().config.visibilityState).isEqualTo(RoomVisibilityState.Private(JoinRuleItem.PrivateVisibility.Private)) } } @@ -514,7 +514,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() { presenter.test { val initialState = initialState() - initialState.eventSink(ConfigureRoomEvents.SetParentSpace(aSpaceRoom())) + initialState.eventSink(ConfigureRoomEvent.SetParentSpace(aSpaceRoom())) assertThat(awaitError()) } @@ -534,8 +534,8 @@ class ConfigureRoomPresenterTest : RobolectricTest() { presenter.test { val initialState = initialState() - initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PrivateVisibility.Private)) - initialState.eventSink(ConfigureRoomEvents.CreateRoom) + initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PrivateVisibility.Private)) + initialState.eventSink(ConfigureRoomEvent.CreateRoom) assertThat(awaitItem().createRoomAction.isLoading()).isTrue() assertThat(awaitItem().createRoomAction.isSuccess()).isTrue() From e1e2718c54b220fd1e3065d2e979614f0911b69f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 09:52:00 +0200 Subject: [PATCH 015/112] `AccountDeactivationEvents` -> `AccountDeactivationEvent` --- ...nEvents.kt => AccountDeactivationEvent.kt} | 10 +++---- .../impl/AccountDeactivationPresenter.kt | 10 +++---- .../logout/impl/AccountDeactivationState.kt | 2 +- .../AccountDeactivationStatePreviewParam.kt | 2 +- .../logout/impl/AccountDeactivationView.kt | 14 ++++----- .../impl/AccountDeactivationPresenterTest.kt | 30 +++++++++---------- .../impl/AccountDeactivationViewTest.kt | 26 ++++++++-------- 7 files changed, 47 insertions(+), 47 deletions(-) rename features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/{AccountDeactivationEvents.kt => AccountDeactivationEvent.kt} (76%) diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationEvents.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationEvent.kt similarity index 76% rename from features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationEvents.kt rename to features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationEvent.kt index 5ceb97b232..cc1f5093fd 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationEvents.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationEvent.kt @@ -8,9 +8,9 @@ package io.element.android.features.logout.impl -sealed interface AccountDeactivationEvents { - data class SetEraseData(val eraseData: Boolean) : AccountDeactivationEvents - data class SetPassword(val password: String) : AccountDeactivationEvents - data class DeactivateAccount(val isRetry: Boolean) : AccountDeactivationEvents - data object CloseDialogs : AccountDeactivationEvents +sealed interface AccountDeactivationEvent { + data class SetEraseData(val eraseData: Boolean) : AccountDeactivationEvent + data class SetPassword(val password: String) : AccountDeactivationEvent + data class DeactivateAccount(val isRetry: Boolean) : AccountDeactivationEvent + data object CloseDialogs : AccountDeactivationEvent } diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenter.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenter.kt index eaeacd022c..11e658aa7f 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenter.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenter.kt @@ -34,19 +34,19 @@ class AccountDeactivationPresenter( val formState = remember { mutableStateOf(DeactivateFormState.Default) } - fun handleEvent(event: AccountDeactivationEvents) { + fun handleEvent(event: AccountDeactivationEvent) { when (event) { - is AccountDeactivationEvents.SetEraseData -> { + is AccountDeactivationEvent.SetEraseData -> { updateFormState(formState) { copy(eraseData = event.eraseData) } } - is AccountDeactivationEvents.SetPassword -> { + is AccountDeactivationEvent.SetPassword -> { updateFormState(formState) { copy(password = event.password) } } - is AccountDeactivationEvents.DeactivateAccount -> + is AccountDeactivationEvent.DeactivateAccount -> if (action.value.isConfirming() || event.isRetry) { localCoroutineScope.deactivateAccount( formState = formState.value, @@ -55,7 +55,7 @@ class AccountDeactivationPresenter( } else { action.value = AsyncAction.ConfirmingNoParams } - AccountDeactivationEvents.CloseDialogs -> { + AccountDeactivationEvent.CloseDialogs -> { action.value = AsyncAction.Uninitialized } } diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationState.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationState.kt index ca9751efcc..ebf9fec3c4 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationState.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationState.kt @@ -15,7 +15,7 @@ import kotlinx.parcelize.Parcelize data class AccountDeactivationState( val deactivateFormState: DeactivateFormState, val accountDeactivationAction: AsyncAction, - val eventSink: (AccountDeactivationEvents) -> Unit, + val eventSink: (AccountDeactivationEvent) -> Unit, ) { val submitEnabled: Boolean get() = accountDeactivationAction is AsyncAction.Uninitialized && diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationStatePreviewParam.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationStatePreviewParam.kt index bd86af9572..b5ab948b05 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationStatePreviewParam.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationStatePreviewParam.kt @@ -45,7 +45,7 @@ internal fun aDeactivateFormState( internal fun anAccountDeactivationState( deactivateFormState: DeactivateFormState = aDeactivateFormState(), accountDeactivationAction: AsyncAction = AsyncAction.Uninitialized, - eventSink: (AccountDeactivationEvents) -> Unit = {}, + eventSink: (AccountDeactivationEvent) -> Unit = {}, ) = AccountDeactivationState( deactivateFormState = deactivateFormState, accountDeactivationAction = accountDeactivationAction, diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt index eb6b401667..d86eae41ff 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt @@ -102,14 +102,14 @@ fun AccountDeactivationView( Content( state = state, onSubmitClick = { - eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) + eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false)) } ) Spacer(modifier = Modifier.height(32.dp)) Buttons( state = state, onSubmitClick = { - eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) + eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false)) } ) } @@ -117,13 +117,13 @@ fun AccountDeactivationView( AccountDeactivationActionDialog( state.accountDeactivationAction, onConfirmClick = { - eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) + eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false)) }, onRetryClick = { - eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = true)) + eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = true)) }, onDismissDialog = { - eventSink(AccountDeactivationEvents.CloseDialogs) + eventSink(AccountDeactivationEvent.CloseDialogs) }, ) } @@ -239,7 +239,7 @@ private fun Content( headline = stringResource(R.string.screen_deactivate_account_delete_all_messages), value = eraseData, onChange = { - eventSink(AccountDeactivationEvents.SetEraseData(it)) + eventSink(AccountDeactivationEvent.SetEraseData(it)) }, enabled = !isLoading, ) @@ -275,7 +275,7 @@ private fun Content( onValueChange = { val sanitized = it.sanitize() passwordFieldState = sanitized - eventSink(AccountDeactivationEvents.SetPassword(sanitized)) + eventSink(AccountDeactivationEvent.SetPassword(sanitized)) }, placeholder = stringResource(CommonStrings.common_password), visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), diff --git a/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenterTest.kt b/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenterTest.kt index ee7f8e470d..d6402c2f59 100644 --- a/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenterTest.kt +++ b/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenterTest.kt @@ -47,11 +47,11 @@ class AccountDeactivationPresenterTest { }.test { val initialState = awaitItem() assertThat(initialState.deactivateFormState).isEqualTo(DeactivateFormState.Default) - initialState.eventSink(AccountDeactivationEvents.SetEraseData(true)) + initialState.eventSink(AccountDeactivationEvent.SetEraseData(true)) val updatedState = awaitItem() assertThat(updatedState.deactivateFormState).isEqualTo(DeactivateFormState.Default.copy(eraseData = true)) assertThat(updatedState.submitEnabled).isFalse() - updatedState.eventSink(AccountDeactivationEvents.SetPassword("password")) + updatedState.eventSink(AccountDeactivationEvent.SetPassword("password")) val updatedState2 = awaitItem() assertThat(updatedState2.deactivateFormState).isEqualTo(DeactivateFormState(password = "password", eraseData = true)) assertThat(updatedState2.submitEnabled).isTrue() @@ -71,12 +71,12 @@ class AccountDeactivationPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(AccountDeactivationEvents.SetPassword("password")) + initialState.eventSink(AccountDeactivationEvent.SetPassword("password")) skipItems(1) - initialState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) + initialState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false)) val updatedState = awaitItem() assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.ConfirmingNoParams) - updatedState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) + updatedState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false)) val updatedState2 = awaitItem() assertThat(updatedState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading) val finalState = awaitItem() @@ -98,20 +98,20 @@ class AccountDeactivationPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(AccountDeactivationEvents.SetPassword("password")) - initialState.eventSink(AccountDeactivationEvents.SetEraseData(true)) + initialState.eventSink(AccountDeactivationEvent.SetPassword("password")) + initialState.eventSink(AccountDeactivationEvent.SetEraseData(true)) skipItems(2) - initialState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) + initialState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false)) val updatedState = awaitItem() assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.ConfirmingNoParams) - updatedState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) + updatedState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false)) val updatedState2 = awaitItem() assertThat(updatedState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading) val finalState = awaitItem() assertThat(finalState.accountDeactivationAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) recorder.assertions().isCalledOnce().with(value("password"), value(true)) // Retry - finalState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = true)) + finalState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = true)) val finalState2 = awaitItem() assertThat(finalState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading) assertThat(awaitItem().accountDeactivationAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) @@ -131,20 +131,20 @@ class AccountDeactivationPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(AccountDeactivationEvents.SetPassword("password")) - initialState.eventSink(AccountDeactivationEvents.SetEraseData(true)) + initialState.eventSink(AccountDeactivationEvent.SetPassword("password")) + initialState.eventSink(AccountDeactivationEvent.SetEraseData(true)) skipItems(2) - initialState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) + initialState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false)) val updatedState = awaitItem() assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.ConfirmingNoParams) - updatedState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) + updatedState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false)) val updatedState2 = awaitItem() assertThat(updatedState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading) val finalState = awaitItem() assertThat(finalState.accountDeactivationAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) recorder.assertions().isCalledOnce().with(value("password"), value(true)) // Cancel - finalState.eventSink(AccountDeactivationEvents.CloseDialogs) + finalState.eventSink(AccountDeactivationEvent.CloseDialogs) val finalState2 = awaitItem() assertThat(finalState2.accountDeactivationAction).isEqualTo(AsyncAction.Uninitialized) } diff --git a/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationViewTest.kt b/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationViewTest.kt index caf4c0384f..5c650fea9d 100644 --- a/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationViewTest.kt +++ b/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationViewTest.kt @@ -35,7 +35,7 @@ import org.robolectric.annotation.Config class AccountDeactivationViewTest : RobolectricTest() { @Test fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setAccountDeactivationView( state = anAccountDeactivationState(eventSink = eventsRecorder), @@ -48,7 +48,7 @@ class AccountDeactivationViewTest : RobolectricTest() { @Config(qualifiers = "h1024dp") @Test fun `clicking on Deactivate emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setAccountDeactivationView( state = anAccountDeactivationState( deactivateFormState = aDeactivateFormState( @@ -58,12 +58,12 @@ class AccountDeactivationViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_delete) - eventsRecorder.assertSingle(AccountDeactivationEvents.DeactivateAccount(false)) + eventsRecorder.assertSingle(AccountDeactivationEvent.DeactivateAccount(false)) } @Test fun `clicking on Deactivate on the confirmation dialog emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setAccountDeactivationView( state = anAccountDeactivationState( deactivateFormState = aDeactivateFormState( @@ -74,12 +74,12 @@ class AccountDeactivationViewTest : RobolectricTest() { ), ) pressTag(TestTags.dialogPositive.value) - eventsRecorder.assertSingle(AccountDeactivationEvents.DeactivateAccount(false)) + eventsRecorder.assertSingle(AccountDeactivationEvent.DeactivateAccount(false)) } @Test fun `clicking on retry on the confirmation dialog emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setAccountDeactivationView( state = anAccountDeactivationState( deactivateFormState = aDeactivateFormState( @@ -90,24 +90,24 @@ class AccountDeactivationViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_retry) - eventsRecorder.assertSingle(AccountDeactivationEvents.DeactivateAccount(true)) + eventsRecorder.assertSingle(AccountDeactivationEvent.DeactivateAccount(true)) } @Test fun `switching on the erase all switch emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setAccountDeactivationView( state = anAccountDeactivationState( eventSink = eventsRecorder, ), ) clickOn(R.string.screen_deactivate_account_delete_all_messages) - eventsRecorder.assertSingle(AccountDeactivationEvents.SetEraseData(true)) + eventsRecorder.assertSingle(AccountDeactivationEvent.SetEraseData(true)) } @Test fun `switching off the erase all switch emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setAccountDeactivationView( state = anAccountDeactivationState( deactivateFormState = aDeactivateFormState( @@ -117,13 +117,13 @@ class AccountDeactivationViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_deactivate_account_delete_all_messages) - eventsRecorder.assertSingle(AccountDeactivationEvents.SetEraseData(false)) + eventsRecorder.assertSingle(AccountDeactivationEvent.SetEraseData(false)) } @Config(qualifiers = "h1024dp") @Test fun `typing text in the password field emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setAccountDeactivationView( state = anAccountDeactivationState( deactivateFormState = aDeactivateFormState( @@ -133,7 +133,7 @@ class AccountDeactivationViewTest : RobolectricTest() { ), ) onNodeWithTag(TestTags.loginPassword.value).performTextInput("A") - eventsRecorder.assertSingle(AccountDeactivationEvents.SetPassword("A$A_PASSWORD")) + eventsRecorder.assertSingle(AccountDeactivationEvent.SetPassword("A$A_PASSWORD")) } } From 17a3db246f734493612c546d648da894aafff052 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 09:52:38 +0200 Subject: [PATCH 016/112] `ForwardMessagesEvents` -> `ForwardMessagesEvent` --- .../{ForwardMessagesEvents.kt => ForwardMessagesEvent.kt} | 4 ++-- .../features/forward/impl/ForwardMessagesPresenter.kt | 4 ++-- .../android/features/forward/impl/ForwardMessagesState.kt | 2 +- .../forward/impl/ForwardMessagesStatePreviewParam.kt | 2 +- .../android/features/forward/impl/ForwardMessagesView.kt | 2 +- .../features/forward/impl/ForwardMessagesPresenterTest.kt | 2 +- .../features/forward/impl/ForwardMessagesViewTest.kt | 6 +++--- 7 files changed, 11 insertions(+), 11 deletions(-) rename features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/{ForwardMessagesEvents.kt => ForwardMessagesEvent.kt} (76%) diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesEvents.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesEvent.kt similarity index 76% rename from features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesEvents.kt rename to features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesEvent.kt index 0161a5d657..9fab72424f 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesEvents.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesEvent.kt @@ -8,6 +8,6 @@ package io.element.android.features.forward.impl -sealed interface ForwardMessagesEvents { - data object ClearError : ForwardMessagesEvents +sealed interface ForwardMessagesEvent { + data object ClearError : ForwardMessagesEvent } diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenter.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenter.kt index 1145f294a3..6068866653 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenter.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenter.kt @@ -48,9 +48,9 @@ class ForwardMessagesPresenter( @Composable override fun present(): ForwardMessagesState { - fun handleEvent(event: ForwardMessagesEvents) { + fun handleEvent(event: ForwardMessagesEvent) { when (event) { - ForwardMessagesEvents.ClearError -> forwardingActionState.value = AsyncAction.Uninitialized + ForwardMessagesEvent.ClearError -> forwardingActionState.value = AsyncAction.Uninitialized } } diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesState.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesState.kt index b1e46adb03..141c1bad38 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesState.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesState.kt @@ -13,5 +13,5 @@ import io.element.android.libraries.matrix.api.core.RoomId data class ForwardMessagesState( val forwardAction: AsyncAction>, - val eventSink: (ForwardMessagesEvents) -> Unit + val eventSink: (ForwardMessagesEvent) -> Unit ) diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesStatePreviewParam.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesStatePreviewParam.kt index 8ff621c2a6..fc61925fcc 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesStatePreviewParam.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesStatePreviewParam.kt @@ -32,7 +32,7 @@ open class ForwardMessagesStatePreviewParam : PreviewParameterProvider> = AsyncAction.Uninitialized, - eventSink: (ForwardMessagesEvents) -> Unit = {} + eventSink: (ForwardMessagesEvent) -> Unit = {} ) = ForwardMessagesState( forwardAction = forwardAction, eventSink = eventSink diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesView.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesView.kt index f350c5b516..8c622d8e29 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesView.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesView.kt @@ -31,7 +31,7 @@ fun ForwardMessagesView( stringResource(id = CommonStrings.error_unknown) }, onErrorDismiss = { - state.eventSink(ForwardMessagesEvents.ClearError) + state.eventSink(ForwardMessagesEvent.ClearError) }, ) } diff --git a/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenterTest.kt b/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenterTest.kt index 5e2f74ccad..0ff3210f2e 100644 --- a/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenterTest.kt +++ b/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenterTest.kt @@ -86,7 +86,7 @@ class ForwardMessagesPresenterTest { val failedForwardState = awaitItem() assertThat(failedForwardState.forwardAction.isFailure()).isTrue() // Then clear error - failedForwardState.eventSink(ForwardMessagesEvents.ClearError) + failedForwardState.eventSink(ForwardMessagesEvent.ClearError) assertThat(awaitItem().forwardAction.isUninitialized()).isTrue() forwardEventLambda.assertions().isCalledOnce() } diff --git a/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/ForwardMessagesViewTest.kt b/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/ForwardMessagesViewTest.kt index a73478479e..3302e3430f 100644 --- a/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/ForwardMessagesViewTest.kt +++ b/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/ForwardMessagesViewTest.kt @@ -29,7 +29,7 @@ import org.junit.Test class ForwardMessagesViewTest : RobolectricTest() { @Test fun `cancel error emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setForwardMessagesView( aForwardMessagesState( forwardAction = AsyncAction.Failure(AN_EXCEPTION), @@ -37,13 +37,13 @@ class ForwardMessagesViewTest : RobolectricTest() { ), ) pressTag(TestTags.dialogPositive.value) - eventsRecorder.assertSingle(ForwardMessagesEvents.ClearError) + eventsRecorder.assertSingle(ForwardMessagesEvent.ClearError) } @Test fun `success invokes onForwardSuccess`() = runAndroidComposeUiTest { val data = listOf(A_ROOM_ID) - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithParam?>(data) { callback -> setForwardMessagesView( aForwardMessagesState( From 3ae9ba74b4e5718b2a7f487dfabd1392abe52ce0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 09:53:16 +0200 Subject: [PATCH 017/112] `NotificationsOptInEvents` -> `NotificationsOptInEvent` --- ...ficationsOptInEvents.kt => NotificationsOptInEvent.kt} | 6 +++--- .../impl/notifications/NotificationsOptInPresenter.kt | 6 +++--- .../ftue/impl/notifications/NotificationsOptInState.kt | 2 +- .../ftue/impl/notifications/NotificationsOptInView.kt | 4 ++-- .../impl/notifications/NotificationsOptInPresenterTest.kt | 8 ++++---- 5 files changed, 13 insertions(+), 13 deletions(-) rename features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/{NotificationsOptInEvents.kt => NotificationsOptInEvent.kt} (65%) diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInEvents.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInEvent.kt similarity index 65% rename from features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInEvents.kt rename to features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInEvent.kt index e0bd5acf43..3ce3b5b2cf 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInEvents.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInEvent.kt @@ -8,7 +8,7 @@ package io.element.android.features.ftue.impl.notifications -sealed interface NotificationsOptInEvents { - data object ContinueClicked : NotificationsOptInEvents - data object NotNowClicked : NotificationsOptInEvents +sealed interface NotificationsOptInEvent { + data object ContinueClicked : NotificationsOptInEvent + data object NotNowClicked : NotificationsOptInEvent } diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenter.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenter.kt index deb7fc59b9..45365670f7 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenter.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenter.kt @@ -52,16 +52,16 @@ class NotificationsOptInPresenter( override fun present(): NotificationsOptInState { val notificationsPermissionsState = postNotificationPermissionsPresenter.present() - fun handleEvent(event: NotificationsOptInEvents) { + fun handleEvent(event: NotificationsOptInEvent) { when (event) { - NotificationsOptInEvents.ContinueClicked -> { + NotificationsOptInEvent.ContinueClicked -> { if (notificationsPermissionsState.permissionGranted) { callback.onNotificationsOptInFinished() } else { notificationsPermissionsState.eventSink(PermissionsEvent.RequestPermissions) } } - NotificationsOptInEvents.NotNowClicked -> { + NotificationsOptInEvent.NotNowClicked -> { if (buildVersionSdkIntProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) { appCoroutineScope.setPermissionDenied() } diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInState.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInState.kt index d12cec0778..cff99ab6df 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInState.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInState.kt @@ -12,5 +12,5 @@ import io.element.android.libraries.permissions.api.PermissionsState data class NotificationsOptInState( val notificationsPermissionState: PermissionsState, - val eventSink: (NotificationsOptInEvents) -> Unit + val eventSink: (NotificationsOptInEvent) -> Unit ) diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt index e2eabe8f2b..e2db8ce4b7 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt @@ -86,14 +86,14 @@ private fun NotificationsOptInFooter(state: NotificationsOptInState) { modifier = Modifier.fillMaxWidth(), text = stringResource(CommonStrings.action_ok), onClick = { - state.eventSink(NotificationsOptInEvents.ContinueClicked) + state.eventSink(NotificationsOptInEvent.ContinueClicked) } ) TextButton( modifier = Modifier.fillMaxWidth(), text = stringResource(CommonStrings.action_not_now), onClick = { - state.eventSink(NotificationsOptInEvents.NotNowClicked) + state.eventSink(NotificationsOptInEvent.NotNowClicked) } ) } diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTest.kt b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTest.kt index 32d68dd1e1..a456d9f373 100644 --- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTest.kt +++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTest.kt @@ -55,7 +55,7 @@ class NotificationsOptInPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(NotificationsOptInEvents.ContinueClicked) + initialState.eventSink(NotificationsOptInEvent.ContinueClicked) assertThat(awaitItem().notificationsPermissionState.showDialog).isTrue() } } @@ -70,7 +70,7 @@ class NotificationsOptInPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(NotificationsOptInEvents.ContinueClicked) + initialState.eventSink(NotificationsOptInEvent.ContinueClicked) assertThat(isFinished).isTrue() } } @@ -86,7 +86,7 @@ class NotificationsOptInPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(NotificationsOptInEvents.NotNowClicked) + initialState.eventSink(NotificationsOptInEvent.NotNowClicked) assertThat(isFinished).isTrue() } } @@ -105,7 +105,7 @@ class NotificationsOptInPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(NotificationsOptInEvents.NotNowClicked) + initialState.eventSink(NotificationsOptInEvent.NotNowClicked) // Allow background coroutines to run runCurrent() From fe57cfa991b320a1f3d9fbb6349e79431978627b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 09:53:54 +0200 Subject: [PATCH 018/112] `HomeSpacesEvents` -> `HomeSpacesEvent` --- .../impl/spaces/{HomeSpacesEvents.kt => HomeSpacesEvent.kt} | 2 +- .../android/features/home/impl/spaces/HomeSpacesPresenter.kt | 2 +- .../android/features/home/impl/spaces/HomeSpacesState.kt | 2 +- .../features/home/impl/spaces/HomeSpacesStatePreviewParam.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/{HomeSpacesEvents.kt => HomeSpacesEvent.kt} (89%) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesEvents.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesEvent.kt similarity index 89% rename from features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesEvents.kt rename to features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesEvent.kt index 88290417dd..0a32da7c09 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesEvents.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesEvent.kt @@ -8,4 +8,4 @@ package io.element.android.features.home.impl.spaces -sealed interface HomeSpacesEvents +sealed interface HomeSpacesEvent diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesPresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesPresenter.kt index b758a1e593..2e2406be87 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesPresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesPresenter.kt @@ -39,7 +39,7 @@ class HomeSpacesPresenter( seenInvitesStore.seenRoomIds().map { it.toImmutableSet() } }.collectAsState(persistentSetOf()) - fun handleEvent(event: HomeSpacesEvents) { + fun handleEvent(event: HomeSpacesEvent) { // when (event) { } } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesState.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesState.kt index e93f04291e..e1c8827087 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesState.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesState.kt @@ -19,7 +19,7 @@ data class HomeSpacesState( val seenSpaceInvites: ImmutableSet, val hideInvitesAvatar: Boolean, val canExploreSpaces: Boolean, - val eventSink: (HomeSpacesEvents) -> Unit, + val eventSink: (HomeSpacesEvent) -> Unit, ) sealed interface CurrentSpace { diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesStatePreviewParam.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesStatePreviewParam.kt index 57b219d302..8d4acae8b7 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesStatePreviewParam.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesStatePreviewParam.kt @@ -43,7 +43,7 @@ internal fun aHomeSpacesState( seenSpaceInvites: Set = emptySet(), hideInvitesAvatar: Boolean = false, canExploreSpaces: Boolean = true, - eventSink: (HomeSpacesEvents) -> Unit = {}, + eventSink: (HomeSpacesEvent) -> Unit = {}, ) = HomeSpacesState( space = space, spaceRooms = spaceRooms.toImmutableList(), From 91aa9ce24f41d83f255950b99fb00b3c453095e7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 09:54:32 +0200 Subject: [PATCH 019/112] `AcceptDeclineInviteEvents` -> `AcceptDeclineInviteEvent` --- .../home/impl/roomlist/RoomListPresenter.kt | 4 ++-- .../home/impl/roomlist/RoomListPresenterTest.kt | 8 ++++---- ...viteEvents.kt => AcceptDeclineInviteEvent.kt} | 6 +++--- .../acceptdecline/AcceptDeclineInviteState.kt | 2 +- .../AcceptDeclineInviteStatePreviewParam.kt | 2 +- .../AcceptDeclineInvitePresenter.kt | 8 ++++---- .../acceptdecline/AcceptDeclineInviteView.kt | 4 ++-- .../InternalAcceptDeclineInviteEvents.kt | 4 ++-- .../AcceptDeclineInvitePresenterTest.kt | 16 ++++++++-------- .../features/joinroom/impl/JoinRoomPresenter.kt | 6 +++--- .../joinroom/impl/JoinRoomPresenterTest.kt | 8 ++++---- .../features/space/impl/root/SpacePresenter.kt | 6 +++--- .../space/impl/root/SpacePresenterTest.kt | 8 ++++---- 13 files changed, 41 insertions(+), 41 deletions(-) rename features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/{AcceptDeclineInviteEvents.kt => AcceptDeclineInviteEvent.kt} (91%) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenter.kt index 1c21177988..546092ede6 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenter.kt @@ -37,8 +37,8 @@ import io.element.android.features.home.impl.spacefilters.SpaceFiltersState import io.element.android.features.home.impl.spacefilters.into import io.element.android.features.home.impl.spacefilters.selectedFilter import io.element.android.features.invite.api.SeenInvitesStore -import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents.AcceptInvite -import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents.DeclineInvite +import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent.AcceptInvite +import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent.DeclineInvite import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomState diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenterTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenterTest.kt index d2e33c77c1..9bc8719a8b 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenterTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenterTest.kt @@ -25,7 +25,7 @@ import io.element.android.features.home.impl.search.aRoomListSearchState import io.element.android.features.home.impl.spacefilters.SpaceFiltersState import io.element.android.features.home.impl.spacefilters.aDisabledSpaceFiltersState import io.element.android.features.invite.api.SeenInvitesStore -import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents +import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInviteState import io.element.android.features.invite.test.InMemorySeenInvitesStore @@ -474,7 +474,7 @@ class RoomListPresenterTest { @Test fun `present - when a room is invited then accept and decline events are sent to acceptDeclinePresenter`() = runTest { - val eventSinkRecorder = lambdaRecorder { _: AcceptDeclineInviteEvents -> } + val eventSinkRecorder = lambdaRecorder { _: AcceptDeclineInviteEvent -> } val acceptDeclinePresenter = Presenter { anAcceptDeclineInviteState(eventSink = eventSinkRecorder) } @@ -513,8 +513,8 @@ class RoomListPresenterTest { assert(eventSinkRecorder) .isCalledExactly(2) .withSequence( - listOf(value(AcceptDeclineInviteEvents.AcceptInvite(inviteData))), - listOf(value(AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = false))), + listOf(value(AcceptDeclineInviteEvent.AcceptInvite(inviteData))), + listOf(value(AcceptDeclineInviteEvent.DeclineInvite(inviteData, blockUser = false, shouldConfirm = false))), ) } } diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteEvents.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteEvent.kt similarity index 91% rename from features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteEvents.kt rename to features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteEvent.kt index 5691b19ef7..1bf1ffea8a 100644 --- a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteEvents.kt +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteEvent.kt @@ -13,7 +13,7 @@ import io.element.android.features.invite.api.InviteData /** * Events sent to the accept/decline invite presenter, from whichever screen is showing the invitation. */ -interface AcceptDeclineInviteEvents { - data class AcceptInvite(val invite: InviteData) : AcceptDeclineInviteEvents - data class DeclineInvite(val invite: InviteData, val blockUser: Boolean, val shouldConfirm: Boolean) : AcceptDeclineInviteEvents +interface AcceptDeclineInviteEvent { + data class AcceptInvite(val invite: InviteData) : AcceptDeclineInviteEvent + data class DeclineInvite(val invite: InviteData, val blockUser: Boolean, val shouldConfirm: Boolean) : AcceptDeclineInviteEvent } diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteState.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteState.kt index 3bfd0430fe..3e9c549620 100644 --- a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteState.kt +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteState.kt @@ -14,5 +14,5 @@ import io.element.android.libraries.matrix.api.core.RoomId data class AcceptDeclineInviteState( val acceptAction: AsyncAction, val declineAction: AsyncAction, - val eventSink: (AcceptDeclineInviteEvents) -> Unit, + val eventSink: (AcceptDeclineInviteEvent) -> Unit, ) diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteStatePreviewParam.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteStatePreviewParam.kt index 18acc11e5e..ceda9542ef 100644 --- a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteStatePreviewParam.kt +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteStatePreviewParam.kt @@ -14,7 +14,7 @@ import io.element.android.libraries.matrix.api.core.RoomId fun anAcceptDeclineInviteState( acceptAction: AsyncAction = AsyncAction.Uninitialized, declineAction: AsyncAction = AsyncAction.Uninitialized, - eventSink: (AcceptDeclineInviteEvents) -> Unit = {}, + eventSink: (AcceptDeclineInviteEvent) -> Unit = {}, ) = AcceptDeclineInviteState( acceptAction = acceptAction, declineAction = declineAction, diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenter.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenter.kt index 9be255fe1a..69c913e538 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenter.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenter.kt @@ -15,7 +15,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import dev.zacsweers.metro.Inject import io.element.android.features.invite.api.InviteData -import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents +import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState import io.element.android.features.invite.api.acceptdecline.ConfirmingDeclineInvite import io.element.android.features.invite.impl.AcceptInvite @@ -40,13 +40,13 @@ class AcceptDeclineInvitePresenter( val declinedAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } - fun handleEvent(event: AcceptDeclineInviteEvents) { + fun handleEvent(event: AcceptDeclineInviteEvent) { when (event) { - is AcceptDeclineInviteEvents.AcceptInvite -> { + is AcceptDeclineInviteEvent.AcceptInvite -> { localCoroutineScope.acceptInvite(event.invite.roomId, acceptedAction) } - is AcceptDeclineInviteEvents.DeclineInvite -> { + is AcceptDeclineInviteEvent.DeclineInvite -> { val inviteData = event.invite if (event.shouldConfirm) { declinedAction.value = ConfirmingDeclineInvite(inviteData, event.blockUser) diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteView.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteView.kt index c3df984604..612ea00cf5 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteView.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteView.kt @@ -14,7 +14,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.invite.api.InviteData -import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents +import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState import io.element.android.features.invite.api.acceptdecline.ConfirmingDeclineInvite import io.element.android.features.invite.impl.AcceptInvite @@ -77,7 +77,7 @@ fun AcceptDeclineInviteView( blockUser = confirming.blockUser, onConfirmClick = { state.eventSink( - AcceptDeclineInviteEvents.DeclineInvite( + AcceptDeclineInviteEvent.DeclineInvite( confirming.inviteData, blockUser = confirming.blockUser, shouldConfirm = false diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/InternalAcceptDeclineInviteEvents.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/InternalAcceptDeclineInviteEvents.kt index 765c7055d3..01c3fe79bc 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/InternalAcceptDeclineInviteEvents.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/InternalAcceptDeclineInviteEvents.kt @@ -8,9 +8,9 @@ package io.element.android.features.invite.impl.acceptdecline -import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents +import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent -sealed interface InternalAcceptDeclineInviteEvents : AcceptDeclineInviteEvents { +sealed interface InternalAcceptDeclineInviteEvents : AcceptDeclineInviteEvent { data object ClearAcceptActionState : InternalAcceptDeclineInviteEvents data object ClearDeclineActionState : InternalAcceptDeclineInviteEvents } diff --git a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenterTest.kt b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenterTest.kt index ba080293f8..a09ae36a40 100644 --- a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenterTest.kt +++ b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenterTest.kt @@ -10,7 +10,7 @@ package io.element.android.features.invite.impl.acceptdecline import com.google.common.truth.Truth.assertThat import io.element.android.features.invite.api.InviteData -import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents +import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent import io.element.android.features.invite.api.acceptdecline.ConfirmingDeclineInvite import io.element.android.features.invite.impl.AcceptInvite import io.element.android.features.invite.impl.DeclineInvite @@ -51,7 +51,7 @@ class AcceptDeclineInvitePresenterTest { val inviteData = anInviteData() awaitItem().also { state -> state.eventSink( - AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true) + AcceptDeclineInviteEvent.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true) ) } awaitItem().also { state -> @@ -78,13 +78,13 @@ class AcceptDeclineInvitePresenterTest { val inviteData = anInviteData() awaitItem().also { state -> state.eventSink( - AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true) + AcceptDeclineInviteEvent.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true) ) } awaitItem().also { state -> assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData, false)) state.eventSink( - AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = false) + AcceptDeclineInviteEvent.DeclineInvite(inviteData, blockUser = false, shouldConfirm = false) ) } assertThat(awaitItem().declineAction.isLoading()).isTrue() @@ -114,13 +114,13 @@ class AcceptDeclineInvitePresenterTest { val inviteData = anInviteData() awaitItem().also { state -> state.eventSink( - AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true) + AcceptDeclineInviteEvent.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true) ) } awaitItem().also { state -> assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData, blockUser = false)) state.eventSink( - AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = false) + AcceptDeclineInviteEvent.DeclineInvite(inviteData, blockUser = false, shouldConfirm = false) ) } assertThat(awaitItem().declineAction.isLoading()).isTrue() @@ -146,7 +146,7 @@ class AcceptDeclineInvitePresenterTest { val inviteData = anInviteData() awaitItem().also { state -> state.eventSink( - AcceptDeclineInviteEvents.AcceptInvite(inviteData) + AcceptDeclineInviteEvent.AcceptInvite(inviteData) ) } awaitItem().also { state -> @@ -178,7 +178,7 @@ class AcceptDeclineInvitePresenterTest { val inviteData = anInviteData() awaitItem().also { state -> state.eventSink( - AcceptDeclineInviteEvents.AcceptInvite(inviteData) + AcceptDeclineInviteEvent.AcceptInvite(inviteData) ) } awaitItem().also { state -> diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt index d257952209..1549944d17 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt @@ -26,7 +26,7 @@ import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.features.invite.api.InviteData import io.element.android.features.invite.api.SeenInvitesStore -import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents +import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState import io.element.android.features.invite.api.toInviteData import io.element.android.features.joinroom.impl.di.CancelKnockRoom @@ -157,12 +157,12 @@ class JoinRoomPresenter( is JoinRoomEvents.KnockRoom -> coroutineScope.knockRoom(knockAction, knockMessage) is JoinRoomEvents.AcceptInvite -> { acceptDeclineInviteState.eventSink( - AcceptDeclineInviteEvents.AcceptInvite(event.inviteData) + AcceptDeclineInviteEvent.AcceptInvite(event.inviteData) ) } is JoinRoomEvents.DeclineInvite -> { acceptDeclineInviteState.eventSink( - AcceptDeclineInviteEvents.DeclineInvite(invite = event.inviteData, blockUser = event.blockUser, shouldConfirm = true) + AcceptDeclineInviteEvent.DeclineInvite(invite = event.inviteData, blockUser = event.blockUser, shouldConfirm = true) ) } is JoinRoomEvents.CancelKnock -> coroutineScope.cancelKnockRoom(event.requiresConfirmation, cancelKnockAction) diff --git a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt index 41ed2cce40..a73a983034 100644 --- a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt +++ b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt @@ -12,7 +12,7 @@ import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.features.invite.api.InviteData import io.element.android.features.invite.api.SeenInvitesStore -import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents +import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInviteState import io.element.android.features.invite.api.toInviteData @@ -365,7 +365,7 @@ class JoinRoomPresenterTest { @Test fun `present - when room is invited then accept and decline events are sent to acceptDeclinePresenter`() = runTest { - val eventSinkRecorder = lambdaRecorder { _: AcceptDeclineInviteEvents -> } + val eventSinkRecorder = lambdaRecorder { _: AcceptDeclineInviteEvent -> } val acceptDeclinePresenter = Presenter { anAcceptDeclineInviteState(eventSink = eventSinkRecorder) } @@ -394,8 +394,8 @@ class JoinRoomPresenterTest { assert(eventSinkRecorder) .isCalledExactly(2) .withSequence( - listOf(value(AcceptDeclineInviteEvents.AcceptInvite(inviteData))), - listOf(value(AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true))), + listOf(value(AcceptDeclineInviteEvent.AcceptInvite(inviteData))), + listOf(value(AcceptDeclineInviteEvent.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true))), ) } } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt index de30ddb6c1..dd5704e0d6 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt @@ -22,7 +22,7 @@ import androidx.compose.runtime.setValue import dev.zacsweers.metro.Inject import im.vector.app.features.analytics.plan.JoinedRoom.Trigger import io.element.android.features.invite.api.SeenInvitesStore -import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents +import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState import io.element.android.features.invite.api.toInviteData import io.element.android.libraries.architecture.AsyncAction @@ -165,12 +165,12 @@ class SpacePresenter( } is SpaceEvents.AcceptInvite -> { acceptDeclineInviteState.eventSink( - AcceptDeclineInviteEvents.AcceptInvite(event.spaceRoom.toInviteData()) + AcceptDeclineInviteEvent.AcceptInvite(event.spaceRoom.toInviteData()) ) } is SpaceEvents.DeclineInvite -> { acceptDeclineInviteState.eventSink( - AcceptDeclineInviteEvents.DeclineInvite(invite = event.spaceRoom.toInviteData(), shouldConfirm = true, blockUser = false) + AcceptDeclineInviteEvent.DeclineInvite(invite = event.spaceRoom.toInviteData(), shouldConfirm = true, blockUser = false) ) } SpaceEvents.HideTopicViewer -> topicViewerState = TopicViewerState.Hidden diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt index 2de52a600b..6ece20f455 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt @@ -15,7 +15,7 @@ import com.google.testing.junit.testparameterinjector.KotlinTestParameters.named import com.google.testing.junit.testparameterinjector.TestParameter import com.google.testing.junit.testparameterinjector.TestParameterInjector import io.element.android.features.invite.api.SeenInvitesStore -import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents +import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInviteState import io.element.android.features.invite.api.toInviteData @@ -272,7 +272,7 @@ class SpacePresenterTest { "decline" to false, ), ) = runTest { - val eventRecorder = EventsRecorder() + val eventRecorder = EventsRecorder() val anInvitedRoom = aSpaceRoom( roomId = A_ROOM_ID_2, state = CurrentUserMembership.INVITED, @@ -302,14 +302,14 @@ class SpacePresenterTest { if (acceptInvite) { state.eventSink(SpaceEvents.AcceptInvite(anInvitedRoom)) eventRecorder.assertSingle( - AcceptDeclineInviteEvents.AcceptInvite( + AcceptDeclineInviteEvent.AcceptInvite( invite = anInvitedRoom.toInviteData(), ) ) } else { state.eventSink(SpaceEvents.DeclineInvite(anInvitedRoom)) eventRecorder.assertSingle( - AcceptDeclineInviteEvents.DeclineInvite( + AcceptDeclineInviteEvent.DeclineInvite( invite = anInvitedRoom.toInviteData(), shouldConfirm = true, blockUser = false, From e395efe730cdcd26c7486bece1467ebcb0508ced Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 09:55:09 +0200 Subject: [PATCH 020/112] `InternalAcceptDeclineInviteEvents` -> `InternalAcceptDeclineInviteEvent` --- .../impl/acceptdecline/AcceptDeclineInvitePresenter.kt | 4 ++-- .../impl/acceptdecline/AcceptDeclineInviteView.kt | 10 +++++----- ...teEvents.kt => InternalAcceptDeclineInviteEvent.kt} | 6 +++--- .../acceptdecline/AcceptDeclineInvitePresenterTest.kt | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) rename features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/{InternalAcceptDeclineInviteEvents.kt => InternalAcceptDeclineInviteEvent.kt} (83%) diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenter.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenter.kt index 69c913e538..e5ee3d20c7 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenter.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenter.kt @@ -58,11 +58,11 @@ class AcceptDeclineInvitePresenter( ) } } - is InternalAcceptDeclineInviteEvents.ClearAcceptActionState -> { + is InternalAcceptDeclineInviteEvent.ClearAcceptActionState -> { acceptedAction.value = AsyncAction.Uninitialized } - is InternalAcceptDeclineInviteEvents.ClearDeclineActionState -> { + is InternalAcceptDeclineInviteEvent.ClearDeclineActionState -> { declinedAction.value = AsyncAction.Uninitialized } } diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteView.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteView.kt index 612ea00cf5..21c59b1860 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteView.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteView.kt @@ -37,11 +37,11 @@ fun AcceptDeclineInviteView( AsyncActionView( async = state.acceptAction, onSuccess = { roomId -> - state.eventSink(InternalAcceptDeclineInviteEvents.ClearAcceptActionState) + state.eventSink(InternalAcceptDeclineInviteEvent.ClearAcceptActionState) onAcceptInviteSuccess(roomId) }, onErrorDismiss = { - state.eventSink(InternalAcceptDeclineInviteEvents.ClearAcceptActionState) + state.eventSink(InternalAcceptDeclineInviteEvent.ClearAcceptActionState) }, errorTitle = { stringResource(CommonStrings.common_something_went_wrong) @@ -57,11 +57,11 @@ fun AcceptDeclineInviteView( AsyncActionView( async = state.declineAction, onSuccess = { roomId -> - state.eventSink(InternalAcceptDeclineInviteEvents.ClearDeclineActionState) + state.eventSink(InternalAcceptDeclineInviteEvent.ClearDeclineActionState) onDeclineInviteSuccess(roomId) }, onErrorDismiss = { - state.eventSink(InternalAcceptDeclineInviteEvents.ClearDeclineActionState) + state.eventSink(InternalAcceptDeclineInviteEvent.ClearDeclineActionState) }, errorTitle = { stringResource(CommonStrings.common_something_went_wrong) @@ -85,7 +85,7 @@ fun AcceptDeclineInviteView( ) }, onDismissClick = { - state.eventSink(InternalAcceptDeclineInviteEvents.ClearDeclineActionState) + state.eventSink(InternalAcceptDeclineInviteEvent.ClearDeclineActionState) } ) } diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/InternalAcceptDeclineInviteEvents.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/InternalAcceptDeclineInviteEvent.kt similarity index 83% rename from features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/InternalAcceptDeclineInviteEvents.kt rename to features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/InternalAcceptDeclineInviteEvent.kt index 01c3fe79bc..805ed9871e 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/InternalAcceptDeclineInviteEvents.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/InternalAcceptDeclineInviteEvent.kt @@ -10,7 +10,7 @@ package io.element.android.features.invite.impl.acceptdecline import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent -sealed interface InternalAcceptDeclineInviteEvents : AcceptDeclineInviteEvent { - data object ClearAcceptActionState : InternalAcceptDeclineInviteEvents - data object ClearDeclineActionState : InternalAcceptDeclineInviteEvents +sealed interface InternalAcceptDeclineInviteEvent : AcceptDeclineInviteEvent { + data object ClearAcceptActionState : InternalAcceptDeclineInviteEvent + data object ClearDeclineActionState : InternalAcceptDeclineInviteEvent } diff --git a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenterTest.kt b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenterTest.kt index a09ae36a40..15624ba9be 100644 --- a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenterTest.kt +++ b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenterTest.kt @@ -57,7 +57,7 @@ class AcceptDeclineInvitePresenterTest { awaitItem().also { state -> assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData, false)) state.eventSink( - InternalAcceptDeclineInviteEvents.ClearDeclineActionState + InternalAcceptDeclineInviteEvent.ClearDeclineActionState ) } awaitItem().also { state -> @@ -91,7 +91,7 @@ class AcceptDeclineInvitePresenterTest { awaitItem().also { state -> assertThat(state.declineAction).isInstanceOf(AsyncAction.Failure::class.java) state.eventSink( - InternalAcceptDeclineInviteEvents.ClearDeclineActionState + InternalAcceptDeclineInviteEvent.ClearDeclineActionState ) } awaitItem().also { state -> @@ -155,7 +155,7 @@ class AcceptDeclineInvitePresenterTest { awaitItem().also { state -> assertThat(state.acceptAction).isInstanceOf(AsyncAction.Failure::class.java) state.eventSink( - InternalAcceptDeclineInviteEvents.ClearAcceptActionState + InternalAcceptDeclineInviteEvent.ClearAcceptActionState ) } awaitItem().also { state -> From 4c56833adad891ea9c33b23d87737cfac51f239b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 09:55:46 +0200 Subject: [PATCH 021/112] `DeclineAndBlockEvents` -> `DeclineAndBlockEvent` --- ...BlockEvents.kt => DeclineAndBlockEvent.kt} | 12 +++++----- .../DeclineAndBlockPresenter.kt | 12 +++++----- .../declineandblock/DeclineAndBlockState.kt | 2 +- .../DeclineAndBlockStatePreviewParam.kt | 2 +- .../declineandblock/DeclineAndBlockView.kt | 12 +++++----- .../DeclineAndBlockPresenterTest.kt | 14 ++++++------ .../DeclineAndBlockViewTest.kt | 22 +++++++++---------- 7 files changed, 38 insertions(+), 38 deletions(-) rename features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/{DeclineAndBlockEvents.kt => DeclineAndBlockEvent.kt} (57%) diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockEvents.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockEvent.kt similarity index 57% rename from features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockEvents.kt rename to features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockEvent.kt index 1fc160e530..f31b9d042f 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockEvents.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockEvent.kt @@ -8,10 +8,10 @@ package io.element.android.features.invite.impl.declineandblock -sealed interface DeclineAndBlockEvents { - data class UpdateReportReason(val reason: String) : DeclineAndBlockEvents - data object ToggleReportRoom : DeclineAndBlockEvents - data object ToggleBlockUser : DeclineAndBlockEvents - data object Decline : DeclineAndBlockEvents - data object ClearDeclineAction : DeclineAndBlockEvents +sealed interface DeclineAndBlockEvent { + data class UpdateReportReason(val reason: String) : DeclineAndBlockEvent + data object ToggleReportRoom : DeclineAndBlockEvent + data object ToggleBlockUser : DeclineAndBlockEvent + data object Decline : DeclineAndBlockEvent + data object ClearDeclineAction : DeclineAndBlockEvent } diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockPresenter.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockPresenter.kt index af2a10cbfd..170d003bea 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockPresenter.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockPresenter.kt @@ -49,13 +49,13 @@ class DeclineAndBlockPresenter( val coroutineScope = rememberCoroutineScope() - fun handleEvent(event: DeclineAndBlockEvents) { + fun handleEvent(event: DeclineAndBlockEvent) { when (event) { - DeclineAndBlockEvents.ClearDeclineAction -> declineAction.value = AsyncAction.Uninitialized - DeclineAndBlockEvents.Decline -> coroutineScope.decline(reportReason, blockUser, reportRoom, declineAction) - DeclineAndBlockEvents.ToggleBlockUser -> blockUser = !blockUser - DeclineAndBlockEvents.ToggleReportRoom -> reportRoom = !reportRoom - is DeclineAndBlockEvents.UpdateReportReason -> reportReason = event.reason + DeclineAndBlockEvent.ClearDeclineAction -> declineAction.value = AsyncAction.Uninitialized + DeclineAndBlockEvent.Decline -> coroutineScope.decline(reportReason, blockUser, reportRoom, declineAction) + DeclineAndBlockEvent.ToggleBlockUser -> blockUser = !blockUser + DeclineAndBlockEvent.ToggleReportRoom -> reportRoom = !reportRoom + is DeclineAndBlockEvent.UpdateReportReason -> reportReason = event.reason } } diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockState.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockState.kt index 7dbbbfd60e..057929ea42 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockState.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockState.kt @@ -15,7 +15,7 @@ data class DeclineAndBlockState( val reportReason: String, val blockUser: Boolean, val declineAction: AsyncAction, - val eventSink: (DeclineAndBlockEvents) -> Unit + val eventSink: (DeclineAndBlockEvent) -> Unit ) { val canDecline = blockUser || reportRoom && reportReason.isNotEmpty() } diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockStatePreviewParam.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockStatePreviewParam.kt index 704fc9f045..6690a568dd 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockStatePreviewParam.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockStatePreviewParam.kt @@ -36,7 +36,7 @@ fun aDeclineAndBlockState( reportReason: String = "", blockUser: Boolean = false, declineAction: AsyncAction = AsyncAction.Uninitialized, - eventSink: (DeclineAndBlockEvents) -> Unit = {}, + eventSink: (DeclineAndBlockEvent) -> Unit = {}, ) = DeclineAndBlockState( reportRoom = reportRoom, reportReason = reportReason, diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockView.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockView.kt index c30742de12..eaace62309 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockView.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockView.kt @@ -56,8 +56,8 @@ fun DeclineAndBlockView( async = state.declineAction, onSuccess = { onDeclineSuccess() }, errorMessage = { stringResource(CommonStrings.error_unknown) }, - onRetry = { state.eventSink(DeclineAndBlockEvents.Decline) }, - onErrorDismiss = { state.eventSink(DeclineAndBlockEvents.ClearDeclineAction) } + onRetry = { state.eventSink(DeclineAndBlockEvent.Decline) }, + onErrorDismiss = { state.eventSink(DeclineAndBlockEvent.ClearDeclineAction) } ) Scaffold( @@ -89,7 +89,7 @@ fun DeclineAndBlockView( Text(text = stringResource(R.string.screen_decline_and_block_block_user_option_description)) }, onClick = { - state.eventSink(DeclineAndBlockEvents.ToggleBlockUser) + state.eventSink(DeclineAndBlockEvent.ToggleBlockUser) }, trailingContent = ListItemContent.Switch(checked = state.blockUser) ) @@ -104,7 +104,7 @@ fun DeclineAndBlockView( Text(text = stringResource(R.string.screen_decline_and_block_report_user_option_description)) }, onClick = { - state.eventSink(DeclineAndBlockEvents.ToggleReportRoom) + state.eventSink(DeclineAndBlockEvent.ToggleReportRoom) }, trailingContent = ListItemContent.Switch(checked = state.reportRoom) ) @@ -113,7 +113,7 @@ fun DeclineAndBlockView( Spacer(modifier = Modifier.height(24.dp)) TextField( value = state.reportReason, - onValueChange = { state.eventSink(DeclineAndBlockEvents.UpdateReportReason(it)) }, + onValueChange = { state.eventSink(DeclineAndBlockEvent.UpdateReportReason(it)) }, placeholder = stringResource(R.string.screen_decline_and_block_report_user_reason_placeholder), minLines = 3, enabled = !isDeclining, @@ -132,7 +132,7 @@ fun DeclineAndBlockView( enabled = !isDeclining && state.canDecline, onClick = { focusManager.clearFocus(force = true) - state.eventSink(DeclineAndBlockEvents.Decline) + state.eventSink(DeclineAndBlockEvent.Decline) }, modifier = Modifier .fillMaxWidth() diff --git a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockPresenterTest.kt b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockPresenterTest.kt index 67c2d55490..61c67c34b3 100644 --- a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockPresenterTest.kt +++ b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockPresenterTest.kt @@ -53,21 +53,21 @@ class DeclineAndBlockPresenterTest { assertThat(state.blockUser).isTrue() assertThat(state.reportReason).isEmpty() assertThat(state.canDecline).isTrue() - state.eventSink(DeclineAndBlockEvents.ToggleBlockUser) + state.eventSink(DeclineAndBlockEvent.ToggleBlockUser) } awaitItem().also { state -> assertThat(state.reportRoom).isFalse() assertThat(state.blockUser).isFalse() assertThat(state.reportReason).isEmpty() assertThat(state.canDecline).isFalse() - state.eventSink(DeclineAndBlockEvents.ToggleReportRoom) + state.eventSink(DeclineAndBlockEvent.ToggleReportRoom) } awaitItem().also { state -> assertThat(state.reportRoom).isTrue() assertThat(state.blockUser).isFalse() assertThat(state.reportReason).isEmpty() assertThat(state.canDecline).isFalse() - state.eventSink(DeclineAndBlockEvents.UpdateReportReason("Spam")) + state.eventSink(DeclineAndBlockEvent.UpdateReportReason("Spam")) } awaitItem().also { state -> assertThat(state.reportRoom).isTrue() @@ -87,7 +87,7 @@ class DeclineAndBlockPresenterTest { ) presenter.test { awaitItem().also { state -> - state.eventSink(DeclineAndBlockEvents.Decline) + state.eventSink(DeclineAndBlockEvent.Decline) } assertThat(awaitItem().declineAction.isLoading()).isTrue() awaitItem().also { state -> @@ -110,12 +110,12 @@ class DeclineAndBlockPresenterTest { ) presenter.test { awaitItem().also { state -> - state.eventSink(DeclineAndBlockEvents.Decline) + state.eventSink(DeclineAndBlockEvent.Decline) } assertThat(awaitItem().declineAction.isLoading()).isTrue() awaitItem().also { state -> assertThat(state.declineAction).isInstanceOf(AsyncAction.Failure::class.java) - state.eventSink(DeclineAndBlockEvents.ClearDeclineAction) + state.eventSink(DeclineAndBlockEvent.ClearDeclineAction) } awaitItem().also { state -> assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java) @@ -137,7 +137,7 @@ class DeclineAndBlockPresenterTest { ) presenter.test { awaitItem().also { state -> - state.eventSink(DeclineAndBlockEvents.Decline) + state.eventSink(DeclineAndBlockEvent.Decline) } assertThat(awaitItem().declineAction.isLoading()).isTrue() awaitItem().also { state -> diff --git a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockViewTest.kt b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockViewTest.kt index 5cfb091e82..d3505ae5b2 100644 --- a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockViewTest.kt +++ b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockViewTest.kt @@ -30,7 +30,7 @@ import org.junit.Test class DeclineAndBlockViewTest : RobolectricTest() { @Test fun `clicking on back invoke the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setDeclineAndBlockView( aDeclineAndBlockState( @@ -44,7 +44,7 @@ class DeclineAndBlockViewTest : RobolectricTest() { @Test fun `a successful decline invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setDeclineAndBlockView( aDeclineAndBlockState( @@ -58,7 +58,7 @@ class DeclineAndBlockViewTest : RobolectricTest() { @Test fun `clicking on decline when enabled emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setDeclineAndBlockView( aDeclineAndBlockState( blockUser = true, @@ -66,12 +66,12 @@ class DeclineAndBlockViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_decline) - eventsRecorder.assertSingle(DeclineAndBlockEvents.Decline) + eventsRecorder.assertSingle(DeclineAndBlockEvent.Decline) } @Test fun `clicking on decline when disabled does not emit event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) setDeclineAndBlockView( aDeclineAndBlockState( blockUser = false, @@ -85,7 +85,7 @@ class DeclineAndBlockViewTest : RobolectricTest() { @Test fun `clicking on block option emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setDeclineAndBlockView( aDeclineAndBlockState( blockUser = true, @@ -93,12 +93,12 @@ class DeclineAndBlockViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_decline_and_block_block_user_option_title) - eventsRecorder.assertSingle(DeclineAndBlockEvents.ToggleBlockUser) + eventsRecorder.assertSingle(DeclineAndBlockEvent.ToggleBlockUser) } @Test fun `clicking on report room option emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setDeclineAndBlockView( aDeclineAndBlockState( reportRoom = true, @@ -106,12 +106,12 @@ class DeclineAndBlockViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_report_room) - eventsRecorder.assertSingle(DeclineAndBlockEvents.ToggleReportRoom) + eventsRecorder.assertSingle(DeclineAndBlockEvent.ToggleReportRoom) } @Test fun `typing text in the reason field emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setDeclineAndBlockView( aDeclineAndBlockState( reportRoom = true, @@ -120,7 +120,7 @@ class DeclineAndBlockViewTest : RobolectricTest() { ), ) onNodeWithText("").performTextInput("Spam!") - eventsRecorder.assertSingle(DeclineAndBlockEvents.UpdateReportReason("Spam!")) + eventsRecorder.assertSingle(DeclineAndBlockEvent.UpdateReportReason("Spam!")) } } From 983d05fc136422856188543ccd0f83c6f5a88c43 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 09:56:23 +0200 Subject: [PATCH 022/112] `InvitePeopleEvents` -> `InvitePeopleEvent` --- .../createroom/impl/addpeople/AddPeopleView.kt | 4 ++-- ...itePeopleEvents.kt => InvitePeopleEvent.kt} | 8 ++++---- .../invitepeople/api/InvitePeopleState.kt | 2 +- .../api/InvitePeopleStatePreviewParam.kt | 4 ++-- .../impl/DefaultInvitePeopleEvents.kt | 4 ++-- .../impl/DefaultInvitePeoplePresenter.kt | 10 +++++----- .../impl/DefaultInvitePeopleState.kt | 4 ++-- .../invitepeople/impl/InvitePeopleView.kt | 4 ++-- .../impl/DefaultInvitePeoplePresenterTest.kt | 18 +++++++++--------- .../impl/invite/RoomInviteMembersNode.kt | 4 ++-- .../impl/invite/RoomInviteMembersView.kt | 6 +++--- 11 files changed, 34 insertions(+), 34 deletions(-) rename features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/{InvitePeopleEvents.kt => InvitePeopleEvent.kt} (68%) 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 f9075691aa..b23226b9c0 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 @@ -21,7 +21,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.createroom.impl.R -import io.element.android.features.invitepeople.api.InvitePeopleEvents +import io.element.android.features.invitepeople.api.InvitePeopleEvent import io.element.android.features.invitepeople.api.InvitePeopleState import io.element.android.features.invitepeople.api.InvitePeopleStatePreviewParam import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage @@ -56,7 +56,7 @@ fun AddPeopleView( Button( text = stringResource(CommonStrings.action_finish), onClick = { - state.eventSink(InvitePeopleEvents.SendInvites) + state.eventSink(InvitePeopleEvent.SendInvites) }, enabled = state.canInvite, modifier = Modifier diff --git a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleEvents.kt b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleEvent.kt similarity index 68% rename from features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleEvents.kt rename to features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleEvent.kt index 9570ab8152..884312da67 100644 --- a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleEvents.kt +++ b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleEvent.kt @@ -11,8 +11,8 @@ package io.element.android.features.invitepeople.api /** * Events the invite people UI sends to its presenter through [InvitePeopleState.eventSink]. */ -interface InvitePeopleEvents { - data object SendInvites : InvitePeopleEvents - data object CloseSearch : InvitePeopleEvents - data object ClearError : InvitePeopleEvents +interface InvitePeopleEvent { + data object SendInvites : InvitePeopleEvent + data object CloseSearch : InvitePeopleEvent + data object ClearError : InvitePeopleEvent } diff --git a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleState.kt b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleState.kt index 0863ec7212..b35ed22f7e 100644 --- a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleState.kt +++ b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleState.kt @@ -28,5 +28,5 @@ interface InvitePeopleState { val createRoomFromDmAction: AsyncAction /** Where the UI sends its events. */ - val eventSink: (InvitePeopleEvents) -> Unit + val eventSink: (InvitePeopleEvent) -> Unit } diff --git a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleStatePreviewParam.kt b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleStatePreviewParam.kt index 860efc2771..379d80440e 100644 --- a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleStatePreviewParam.kt +++ b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleStatePreviewParam.kt @@ -27,7 +27,7 @@ private data class PreviewInvitePeopleState( override val isSearchActive: Boolean, override val sendInvitesAction: AsyncAction, override val createRoomFromDmAction: AsyncAction, - override val eventSink: (InvitePeopleEvents) -> Unit, + override val eventSink: (InvitePeopleEvent) -> Unit, ) : InvitePeopleState private fun aPreviewInvitePeopleState( @@ -35,7 +35,7 @@ private fun aPreviewInvitePeopleState( isSearchActive: Boolean = false, sendInvitesAction: AsyncAction = AsyncAction.Uninitialized, createRoomFromDmAction: AsyncAction = AsyncAction.Uninitialized, - eventSink: (InvitePeopleEvents) -> Unit = {}, + eventSink: (InvitePeopleEvent) -> Unit = {}, ) = PreviewInvitePeopleState( canInvite = canInvite, isSearchActive = isSearchActive, diff --git a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleEvents.kt b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleEvents.kt index 449d0ce6ac..0b6df9134f 100644 --- a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleEvents.kt +++ b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleEvents.kt @@ -8,10 +8,10 @@ package io.element.android.features.invitepeople.impl -import io.element.android.features.invitepeople.api.InvitePeopleEvents +import io.element.android.features.invitepeople.api.InvitePeopleEvent import io.element.android.libraries.matrix.api.user.MatrixUser -sealed interface DefaultInvitePeopleEvents : InvitePeopleEvents { +sealed interface DefaultInvitePeopleEvents : InvitePeopleEvent { data class ToggleUser(val user: MatrixUser) : DefaultInvitePeopleEvents data class OnSearchActiveChanged(val active: Boolean) : DefaultInvitePeopleEvents data object DismissUnknownUsersModal : DefaultInvitePeopleEvents diff --git a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenter.kt b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenter.kt index 79cd0c83be..186de83444 100644 --- a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenter.kt +++ b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenter.kt @@ -24,7 +24,7 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.ContributesBinding -import io.element.android.features.invitepeople.api.InvitePeopleEvents +import io.element.android.features.invitepeople.api.InvitePeopleEvent import io.element.android.features.invitepeople.api.InvitePeoplePresenter import io.element.android.features.invitepeople.api.InvitePeopleState import io.element.android.libraries.architecture.AsyncAction @@ -179,7 +179,7 @@ class DefaultInvitePeoplePresenter( ) } - fun handleEvent(event: InvitePeopleEvents) { + fun handleEvent(event: InvitePeopleEvent) { when (event) { // Dedicated `when` for exhaustivity. is DefaultInvitePeopleEvents -> when (event) { @@ -207,7 +207,7 @@ class DefaultInvitePeoplePresenter( sendInvitesAction.value = AsyncAction.Uninitialized } } - is InvitePeopleEvents.SendInvites -> { + is InvitePeopleEvent.SendInvites -> { if (unknownUsers.isNotEmpty() && sendInvitesAction.value !is ConfirmingUnknownUserInvitation) { sendInvitesAction.value = ConfirmingUnknownUserInvitation( unknownUsers @@ -224,11 +224,11 @@ class DefaultInvitePeoplePresenter( } } } - is InvitePeopleEvents.CloseSearch -> { + is InvitePeopleEvent.CloseSearch -> { searchActive = false queryState.clearText() } - is InvitePeopleEvents.ClearError -> { + is InvitePeopleEvent.ClearError -> { sendInvitesAction.value = AsyncAction.Uninitialized createRoomFromDmAction.value = AsyncAction.Uninitialized } diff --git a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleState.kt b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleState.kt index 46e5d9f1a5..f1058d1a1b 100644 --- a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleState.kt +++ b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleState.kt @@ -9,7 +9,7 @@ package io.element.android.features.invitepeople.impl import androidx.compose.foundation.text.input.TextFieldState -import io.element.android.features.invitepeople.api.InvitePeopleEvents +import io.element.android.features.invitepeople.api.InvitePeopleEvent import io.element.android.features.invitepeople.api.InvitePeopleState import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData @@ -29,5 +29,5 @@ data class DefaultInvitePeopleState( override val sendInvitesAction: AsyncAction, override val createRoomFromDmAction: AsyncAction, val suggestions: ImmutableList, - override val eventSink: (InvitePeopleEvents) -> Unit + override val eventSink: (InvitePeopleEvent) -> Unit ) : InvitePeopleState diff --git a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt index 7dced41b13..053abd6baa 100644 --- a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt +++ b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt @@ -30,7 +30,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.features.invitepeople.api.InvitePeopleEvents +import io.element.android.features.invitepeople.api.InvitePeopleEvent import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.atomic.molecules.ButtonRowMolecule import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule @@ -164,7 +164,7 @@ private fun InvitePeopleContentView( InvitePeopleConfirmModal( users = state.sendInvitesAction.users, onDismiss = { state.eventSink.invoke(DefaultInvitePeopleEvents.DismissUnknownUsersModal) }, - onInvite = { state.eventSink.invoke(InvitePeopleEvents.SendInvites) }, + onInvite = { state.eventSink.invoke(InvitePeopleEvent.SendInvites) }, onRemove = { state.eventSink.invoke(DefaultInvitePeopleEvents.RemoveUnknownUsers) } ) } diff --git a/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt b/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt index 922fa73060..5087ec6eea 100644 --- a/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt +++ b/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt @@ -11,7 +11,7 @@ package io.element.android.features.invitepeople.impl import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd import app.cash.turbine.ReceiveTurbine import com.google.common.truth.Truth.assertThat -import io.element.android.features.invitepeople.api.InvitePeopleEvents +import io.element.android.features.invitepeople.api.InvitePeopleEvent import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.designsystem.theme.components.SearchBarResultState @@ -94,7 +94,7 @@ internal class DefaultInvitePeoplePresenterTest { assertThat(resultState.isSearchActive).isTrue() resultState.searchQuery.setTextAndPlaceCursorAtEnd("some query") assertThat(awaitItemAsDefault().searchQuery.text.toString()).isEqualTo("some query") - resultState.eventSink(InvitePeopleEvents.CloseSearch) + resultState.eventSink(InvitePeopleEvent.CloseSearch) skipItems(1) awaitItemAsDefault().also { assertThat(it.isSearchActive).isFalse() @@ -420,7 +420,7 @@ internal class DefaultInvitePeoplePresenterTest { // The results are updated... assertThat(resultState.searchResults).isInstanceOf(SearchBarResultState.Results::class.java) // Send invites - initialState.eventSink(InvitePeopleEvents.SendInvites) + initialState.eventSink(InvitePeopleEvent.SendInvites) // Can't invite in the loading state awaitItem().run { @@ -474,7 +474,7 @@ internal class DefaultInvitePeoplePresenterTest { // The results are updated... assertThat(resultState.searchResults).isInstanceOf(SearchBarResultState.Results::class.java) // Send invites - initialState.eventSink(InvitePeopleEvents.SendInvites) + initialState.eventSink(InvitePeopleEvent.SendInvites) // Can't invite in the loading state awaitItem().run { @@ -655,7 +655,7 @@ internal class DefaultInvitePeoplePresenterTest { // If we do not have their identity cached, or fail to fetch it, we should mark them as unknown. awaitItemAsDefault().run { assertThat(selectedUsers).containsExactly(alice, bob, charlie) - eventSink(InvitePeopleEvents.SendInvites) + eventSink(InvitePeopleEvent.SendInvites) } getUserIdentityResult.assertions().isCalledExactly(3).withSequence( @@ -668,7 +668,7 @@ internal class DefaultInvitePeoplePresenterTest { awaitItemAsDefault().run { assertThat(sendInvitesAction).isInstanceOf(ConfirmingUnknownUserInvitation::class.java) assertThat(canInvite).isTrue() - eventSink(InvitePeopleEvents.SendInvites) + eventSink(InvitePeopleEvent.SendInvites) } delay(1_000) @@ -738,7 +738,7 @@ internal class DefaultInvitePeoplePresenterTest { searchResults.users().map { Pair(it.matrixUser, it.isSelected) } ).containsExactly(Pair(alice, true), Pair(bob, true)) - eventSink(InvitePeopleEvents.SendInvites) + eventSink(InvitePeopleEvent.SendInvites) } getUserIdentityResult.assertions().isCalledExactly(3).withSequence( @@ -803,7 +803,7 @@ internal class DefaultInvitePeoplePresenterTest { awaitItemAsDefault().run { assertThat(selectedUsers).containsExactly(alice, bob, charlie) - eventSink(InvitePeopleEvents.SendInvites) + eventSink(InvitePeopleEvent.SendInvites) } getUserIdentityResult.assertions().isCalledExactly(3).withSequence( @@ -856,7 +856,7 @@ internal class DefaultInvitePeoplePresenterTest { initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(alice)) // And we send the invites - initialState.eventSink(InvitePeopleEvents.SendInvites) + initialState.eventSink(InvitePeopleEvent.SendInvites) skipItems(1) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersNode.kt index 3919817313..87d0ca7ace 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersNode.kt @@ -20,7 +20,7 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode -import io.element.android.features.invitepeople.api.InvitePeopleEvents +import io.element.android.features.invitepeople.api.InvitePeopleEvent import io.element.android.features.invitepeople.api.InvitePeoplePresenter import io.element.android.features.invitepeople.api.InvitePeopleRenderer import io.element.android.libraries.architecture.callback @@ -81,7 +81,7 @@ class RoomInviteMembersNode( ProgressDialog(text = stringResource(CommonStrings.common_creating_room)) }, onErrorDismiss = { - state.eventSink(InvitePeopleEvents.ClearError) + state.eventSink(InvitePeopleEvent.ClearError) } ) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt index d00a4e159a..ba9d2b4498 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt @@ -22,7 +22,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme -import io.element.android.features.invitepeople.api.InvitePeopleEvents +import io.element.android.features.invitepeople.api.InvitePeopleEvent import io.element.android.features.invitepeople.api.InvitePeopleState import io.element.android.features.invitepeople.api.InvitePeopleStatePreviewParam import io.element.android.features.roomdetails.impl.R @@ -49,13 +49,13 @@ fun RoomInviteMembersView( RoomInviteMembersTopBar( onBackClick = { if (state.isSearchActive) { - state.eventSink(InvitePeopleEvents.CloseSearch) + state.eventSink(InvitePeopleEvent.CloseSearch) } else { onBackClick() } }, onSubmitClick = { - state.eventSink(InvitePeopleEvents.SendInvites) + state.eventSink(InvitePeopleEvent.SendInvites) }, canSend = state.canInvite, ) From 55a7d278c9a4d7317587a9e3f2988cc614023072 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 09:56:59 +0200 Subject: [PATCH 023/112] `DefaultInvitePeopleEvents` -> `DefaultInvitePeopleEvent` --- ...eEvents.kt => DefaultInvitePeopleEvent.kt} | 10 ++--- .../impl/DefaultInvitePeoplePresenter.kt | 10 ++--- .../invitepeople/impl/InvitePeopleView.kt | 10 ++--- .../impl/DefaultInvitePeoplePresenterTest.kt | 40 +++++++++---------- 4 files changed, 35 insertions(+), 35 deletions(-) rename features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/{DefaultInvitePeopleEvents.kt => DefaultInvitePeopleEvent.kt} (78%) diff --git a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleEvents.kt b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleEvent.kt similarity index 78% rename from features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleEvents.kt rename to features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleEvent.kt index 0b6df9134f..90d6fef59d 100644 --- a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleEvents.kt +++ b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleEvent.kt @@ -11,9 +11,9 @@ package io.element.android.features.invitepeople.impl import io.element.android.features.invitepeople.api.InvitePeopleEvent import io.element.android.libraries.matrix.api.user.MatrixUser -sealed interface DefaultInvitePeopleEvents : InvitePeopleEvent { - data class ToggleUser(val user: MatrixUser) : DefaultInvitePeopleEvents - data class OnSearchActiveChanged(val active: Boolean) : DefaultInvitePeopleEvents - data object DismissUnknownUsersModal : DefaultInvitePeopleEvents - data object RemoveUnknownUsers : DefaultInvitePeopleEvents +sealed interface DefaultInvitePeopleEvent : InvitePeopleEvent { + data class ToggleUser(val user: MatrixUser) : DefaultInvitePeopleEvent + data class OnSearchActiveChanged(val active: Boolean) : DefaultInvitePeopleEvent + data object DismissUnknownUsersModal : DefaultInvitePeopleEvent + data object RemoveUnknownUsers : DefaultInvitePeopleEvent } diff --git a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenter.kt b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenter.kt index 186de83444..dbf5c46291 100644 --- a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenter.kt +++ b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenter.kt @@ -182,23 +182,23 @@ class DefaultInvitePeoplePresenter( fun handleEvent(event: InvitePeopleEvent) { when (event) { // Dedicated `when` for exhaustivity. - is DefaultInvitePeopleEvents -> when (event) { - is DefaultInvitePeopleEvents.OnSearchActiveChanged -> { + is DefaultInvitePeopleEvent -> when (event) { + is DefaultInvitePeopleEvent.OnSearchActiveChanged -> { searchActive = event.active if (!event.active) { queryState.clearText() } } - is DefaultInvitePeopleEvents.ToggleUser -> { + is DefaultInvitePeopleEvent.ToggleUser -> { selectedUsers.toggleUser(event.user) searchResults.toggleUser(event.user) // suggestions will automatically update via derivedStateOf when selectedUsers changes } - is DefaultInvitePeopleEvents.DismissUnknownUsersModal -> { + is DefaultInvitePeopleEvent.DismissUnknownUsersModal -> { sendInvitesAction.value = AsyncAction.Uninitialized } - is DefaultInvitePeopleEvents.RemoveUnknownUsers -> { + is DefaultInvitePeopleEvent.RemoveUnknownUsers -> { val usersToRemove = selectedUsers.value.filter { it in unknownUsers } usersToRemove.forEach { user -> selectedUsers.toggleUser(user) diff --git a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt index 053abd6baa..4da2ec7cea 100644 --- a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt +++ b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt @@ -102,7 +102,7 @@ private fun InvitePeopleContentView( verticalArrangement = Arrangement.spacedBy(16.dp), ) { fun toggleUser(user: MatrixUser) { - state.eventSink(DefaultInvitePeopleEvents.ToggleUser(user)) + state.eventSink(DefaultInvitePeopleEvent.ToggleUser(user)) } InvitePeopleSearchBar( @@ -114,7 +114,7 @@ private fun InvitePeopleContentView( active = state.isSearchActive, onActiveChange = { state.eventSink( - DefaultInvitePeopleEvents.OnSearchActiveChanged( + DefaultInvitePeopleEvent.OnSearchActiveChanged( it ) ) @@ -144,7 +144,7 @@ private fun InvitePeopleContentView( CheckableUserRow( checked = invitableUser.isSelected, onCheckedChange = { - state.eventSink(DefaultInvitePeopleEvents.ToggleUser(invitableUser.matrixUser)) + state.eventSink(DefaultInvitePeopleEvent.ToggleUser(invitableUser.matrixUser)) }, data = CheckableUserRowData.Resolved( avatarData = invitableUser.matrixUser.getAvatarData(AvatarSize.UserListItem), @@ -163,9 +163,9 @@ private fun InvitePeopleContentView( if (state.sendInvitesAction is ConfirmingUnknownUserInvitation) { InvitePeopleConfirmModal( users = state.sendInvitesAction.users, - onDismiss = { state.eventSink.invoke(DefaultInvitePeopleEvents.DismissUnknownUsersModal) }, + onDismiss = { state.eventSink.invoke(DefaultInvitePeopleEvent.DismissUnknownUsersModal) }, onInvite = { state.eventSink.invoke(InvitePeopleEvent.SendInvites) }, - onRemove = { state.eventSink.invoke(DefaultInvitePeopleEvents.RemoveUnknownUsers) } + onRemove = { state.eventSink.invoke(DefaultInvitePeopleEvent.RemoveUnknownUsers) } ) } } diff --git a/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt b/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt index 5087ec6eea..39cfc3afba 100644 --- a/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt +++ b/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt @@ -88,7 +88,7 @@ internal class DefaultInvitePeoplePresenterTest { val initialState = awaitItem() skipItems(1) - initialState.eventSink(DefaultInvitePeopleEvents.OnSearchActiveChanged(true)) + initialState.eventSink(DefaultInvitePeopleEvent.OnSearchActiveChanged(true)) val resultState = awaitItemAsDefault() assertThat(resultState.isSearchActive).isTrue() @@ -290,18 +290,18 @@ internal class DefaultInvitePeoplePresenterTest { skipItems(1) // When we toggle a user not in the list, they are added - initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(aMatrixUser())) + initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(aMatrixUser())) assertThat(awaitItemAsDefault().selectedUsers).containsExactly(aMatrixUser()) // Toggling a different user also adds them - initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(aMatrixUser(id = A_USER_ID_2.value))) + initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(aMatrixUser(id = A_USER_ID_2.value))) assertThat(awaitItemAsDefault().selectedUsers).containsExactly( aMatrixUser(), aMatrixUser(id = A_USER_ID_2.value) ) // Toggling the first user removes them - initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(aMatrixUser())) + initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(aMatrixUser())) assertThat(awaitItemAsDefault().selectedUsers).containsExactly(aMatrixUser(id = A_USER_ID_2.value)) } } @@ -319,7 +319,7 @@ internal class DefaultInvitePeoplePresenterTest { val selectedUser = aMatrixUser(displayName = "John Doe") - initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(selectedUser)) + initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(selectedUser)) initialState.searchQuery.setTextAndPlaceCursorAtEnd("some query") skipItems(1) @@ -373,7 +373,7 @@ internal class DefaultInvitePeoplePresenterTest { } // And then a user is toggled - initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(selectedUser)) + initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(selectedUser)) skipItems(1) val resultState = awaitItemAsDefault() @@ -414,7 +414,7 @@ internal class DefaultInvitePeoplePresenterTest { repository.emitStateWithUsers(users = aMatrixUserList() + selectedUser) skipItems(1) // And then a user is toggled - initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(selectedUser)) + initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(selectedUser)) skipItems(1) val resultState = awaitItemAsDefault() // The results are updated... @@ -468,7 +468,7 @@ internal class DefaultInvitePeoplePresenterTest { repository.emitStateWithUsers(users = aMatrixUserList() + selectedUser) skipItems(1) // And then a user is toggled - initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(selectedUser)) + initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(selectedUser)) skipItems(1) val resultState = awaitItemAsDefault() // The results are updated... @@ -645,11 +645,11 @@ internal class DefaultInvitePeoplePresenterTest { skipItems(1) // When we toggle a user not in the list, they are added, and we fetch their identity. - initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(alice)) + initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(alice)) delay(100) - awaitItemAsDefault().eventSink(DefaultInvitePeopleEvents.ToggleUser(bob)) + awaitItemAsDefault().eventSink(DefaultInvitePeopleEvent.ToggleUser(bob)) delay(100) - awaitItemAsDefault().eventSink(DefaultInvitePeopleEvents.ToggleUser(charlie)) + awaitItemAsDefault().eventSink(DefaultInvitePeopleEvent.ToggleUser(charlie)) delay(100) // If we do not have their identity cached, or fail to fetch it, we should mark them as unknown. @@ -712,11 +712,11 @@ internal class DefaultInvitePeoplePresenterTest { skipItems(1) // When we toggle a user not in the list, they are added, and we fetch their identity. - initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(alice)) + initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(alice)) delay(100) - awaitItemAsDefault().eventSink(DefaultInvitePeopleEvents.ToggleUser(bob)) + awaitItemAsDefault().eventSink(DefaultInvitePeopleEvent.ToggleUser(bob)) delay(100) - awaitItemAsDefault().eventSink(DefaultInvitePeopleEvents.ToggleUser(charlie)) + awaitItemAsDefault().eventSink(DefaultInvitePeopleEvent.ToggleUser(charlie)) delay(100) // And the search is matching Alice and Bob @@ -751,7 +751,7 @@ internal class DefaultInvitePeoplePresenterTest { awaitItemAsDefault().run { assertThat(sendInvitesAction).isInstanceOf(ConfirmingUnknownUserInvitation::class.java) assertThat(canInvite).isTrue() - eventSink(DefaultInvitePeopleEvents.RemoveUnknownUsers) + eventSink(DefaultInvitePeopleEvent.RemoveUnknownUsers) } // Selecting "remove" should remove all unknown users, but keeps those who are known. @@ -794,11 +794,11 @@ internal class DefaultInvitePeoplePresenterTest { skipItems(1) // When we toggle a user not in the list, they are added, and we fetch their identity. - initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(alice)) + initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(alice)) delay(100) - awaitItemAsDefault().eventSink(DefaultInvitePeopleEvents.ToggleUser(bob)) + awaitItemAsDefault().eventSink(DefaultInvitePeopleEvent.ToggleUser(bob)) delay(100) - awaitItemAsDefault().eventSink(DefaultInvitePeopleEvents.ToggleUser(charlie)) + awaitItemAsDefault().eventSink(DefaultInvitePeopleEvent.ToggleUser(charlie)) delay(100) awaitItemAsDefault().run { @@ -816,7 +816,7 @@ internal class DefaultInvitePeoplePresenterTest { awaitItemAsDefault().run { assertThat(sendInvitesAction).isInstanceOf(ConfirmingUnknownUserInvitation::class.java) assertThat(canInvite).isTrue() - eventSink(DefaultInvitePeopleEvents.DismissUnknownUsersModal) + eventSink(DefaultInvitePeopleEvent.DismissUnknownUsersModal) } // Dismissing should not modify the selection at all @@ -853,7 +853,7 @@ internal class DefaultInvitePeoplePresenterTest { skipItems(1) // We want to add a new user to a DM - initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(alice)) + initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(alice)) // And we send the invites initialState.eventSink(InvitePeopleEvent.SendInvites) From 19221a8042fbf546151f85a5d32ccc0dbc876781 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 09:57:38 +0200 Subject: [PATCH 024/112] `JoinRoomEvents` -> `JoinRoomEvent` --- .../{JoinRoomEvents.kt => JoinRoomEvent.kt} | 22 ++++---- .../joinroom/impl/JoinRoomPresenter.kt | 22 ++++---- .../features/joinroom/impl/JoinRoomState.kt | 2 +- .../impl/JoinRoomStatePreviewParam.kt | 2 +- .../features/joinroom/impl/JoinRoomView.kt | 30 +++++------ .../joinroom/impl/JoinRoomPresenterTest.kt | 28 +++++----- .../joinroom/impl/JoinRoomViewTest.kt | 52 +++++++++---------- 7 files changed, 79 insertions(+), 79 deletions(-) rename features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/{JoinRoomEvents.kt => JoinRoomEvent.kt} (60%) diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvents.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvent.kt similarity index 60% rename from features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvents.kt rename to features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvent.kt index 51245dd8f6..a2e53485ce 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvents.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvent.kt @@ -10,15 +10,15 @@ package io.element.android.features.joinroom.impl import io.element.android.features.invite.api.InviteData -sealed interface JoinRoomEvents { - data object RetryFetchingContent : JoinRoomEvents - data object DismissErrorAndHideContent : JoinRoomEvents - data object JoinRoom : JoinRoomEvents - data object KnockRoom : JoinRoomEvents - data object ForgetRoom : JoinRoomEvents - data class CancelKnock(val requiresConfirmation: Boolean) : JoinRoomEvents - data class UpdateKnockMessage(val message: String) : JoinRoomEvents - data object ClearActionStates : JoinRoomEvents - data class AcceptInvite(val inviteData: InviteData) : JoinRoomEvents - data class DeclineInvite(val inviteData: InviteData, val blockUser: Boolean) : JoinRoomEvents +sealed interface JoinRoomEvent { + data object RetryFetchingContent : JoinRoomEvent + data object DismissErrorAndHideContent : JoinRoomEvent + data object JoinRoom : JoinRoomEvent + data object KnockRoom : JoinRoomEvent + data object ForgetRoom : JoinRoomEvent + data class CancelKnock(val requiresConfirmation: Boolean) : JoinRoomEvent + data class UpdateKnockMessage(val message: String) : JoinRoomEvent + data object ClearActionStates : JoinRoomEvent + data class AcceptInvite(val inviteData: InviteData) : JoinRoomEvent + data class DeclineInvite(val inviteData: InviteData, val blockUser: Boolean) : JoinRoomEvent } diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt index 1549944d17..85ae9cf392 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt @@ -151,37 +151,37 @@ class JoinRoomPresenter( contentState.markRoomInviteAsSeen() } - fun handleEvent(event: JoinRoomEvents) { + fun handleEvent(event: JoinRoomEvent) { when (event) { - JoinRoomEvents.JoinRoom -> coroutineScope.joinRoom(joinAction) - is JoinRoomEvents.KnockRoom -> coroutineScope.knockRoom(knockAction, knockMessage) - is JoinRoomEvents.AcceptInvite -> { + JoinRoomEvent.JoinRoom -> coroutineScope.joinRoom(joinAction) + is JoinRoomEvent.KnockRoom -> coroutineScope.knockRoom(knockAction, knockMessage) + is JoinRoomEvent.AcceptInvite -> { acceptDeclineInviteState.eventSink( AcceptDeclineInviteEvent.AcceptInvite(event.inviteData) ) } - is JoinRoomEvents.DeclineInvite -> { + is JoinRoomEvent.DeclineInvite -> { acceptDeclineInviteState.eventSink( AcceptDeclineInviteEvent.DeclineInvite(invite = event.inviteData, blockUser = event.blockUser, shouldConfirm = true) ) } - is JoinRoomEvents.CancelKnock -> coroutineScope.cancelKnockRoom(event.requiresConfirmation, cancelKnockAction) - JoinRoomEvents.RetryFetchingContent -> { + is JoinRoomEvent.CancelKnock -> coroutineScope.cancelKnockRoom(event.requiresConfirmation, cancelKnockAction) + JoinRoomEvent.RetryFetchingContent -> { retryCount++ } - JoinRoomEvents.ClearActionStates -> { + JoinRoomEvent.ClearActionStates -> { knockAction.value = AsyncAction.Uninitialized joinAction.value = AsyncAction.Uninitialized cancelKnockAction.value = AsyncAction.Uninitialized forgetRoomAction.value = AsyncAction.Uninitialized } - is JoinRoomEvents.UpdateKnockMessage -> { + is JoinRoomEvent.UpdateKnockMessage -> { knockMessage = event.message.take(MAX_KNOCK_MESSAGE_LENGTH) } - JoinRoomEvents.DismissErrorAndHideContent -> { + JoinRoomEvent.DismissErrorAndHideContent -> { isDismissingContent = true } - JoinRoomEvents.ForgetRoom -> coroutineScope.forgetRoom(forgetRoomAction) + JoinRoomEvent.ForgetRoom -> coroutineScope.forgetRoom(forgetRoomAction) } } diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt index e109bb1315..25f7765a7c 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt @@ -37,7 +37,7 @@ data class JoinRoomState( val knockMessage: String, val hideInviteAvatars: Boolean, val canReportRoom: Boolean, - val eventSink: (JoinRoomEvents) -> Unit + val eventSink: (JoinRoomEvent) -> Unit ) { val isJoinActionUnauthorized = joinAction is AsyncAction.Failure && joinAction.error is JoinRoom.Failures.UnauthorizedJoin val joinAuthorisationStatus = when (contentState) { diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStatePreviewParam.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStatePreviewParam.kt index f47377b16b..a09e33aded 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStatePreviewParam.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStatePreviewParam.kt @@ -207,7 +207,7 @@ fun aJoinRoomState( knockMessage: String = "", hideInviteAvatars: Boolean = false, canReportRoom: Boolean = true, - eventSink: (JoinRoomEvents) -> Unit = {} + eventSink: (JoinRoomEvent) -> Unit = {} ) = JoinRoomState( roomIdOrAlias = roomIdOrAlias, contentState = contentState, diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt index f1adf1705f..3dd964610a 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt @@ -115,33 +115,33 @@ fun JoinRoomView( contentState = state.contentState, knockMessage = state.knockMessage, hideAvatarsImages = state.hideAvatarsImages, - onKnockMessageUpdate = { state.eventSink(JoinRoomEvents.UpdateKnockMessage(it)) }, + onKnockMessageUpdate = { state.eventSink(JoinRoomEvent.UpdateKnockMessage(it)) }, ) }, footer = { JoinRoomFooter( joinAuthorisationStatus = state.joinAuthorisationStatus, onAcceptInvite = { inviteData -> - state.eventSink(JoinRoomEvents.AcceptInvite(inviteData)) + state.eventSink(JoinRoomEvent.AcceptInvite(inviteData)) }, onDeclineInvite = { inviteData, blockUser -> if (state.canReportRoom && blockUser) { onDeclineInviteAndBlockUser(inviteData) } else { - state.eventSink(JoinRoomEvents.DeclineInvite(inviteData, blockUser = blockUser)) + state.eventSink(JoinRoomEvent.DeclineInvite(inviteData, blockUser = blockUser)) } }, onJoinRoom = { - state.eventSink(JoinRoomEvents.JoinRoom) + state.eventSink(JoinRoomEvent.JoinRoom) }, onKnockRoom = { - state.eventSink(JoinRoomEvents.KnockRoom) + state.eventSink(JoinRoomEvent.KnockRoom) }, onCancelKnock = { - state.eventSink(JoinRoomEvents.CancelKnock(requiresConfirmation = true)) + state.eventSink(JoinRoomEvent.CancelKnock(requiresConfirmation = true)) }, onForgetRoom = { - state.eventSink(JoinRoomEvents.ForgetRoom) + state.eventSink(JoinRoomEvent.ForgetRoom) }, onGoBack = onBackClick, ) @@ -152,9 +152,9 @@ fun JoinRoomView( RetryDialog( title = stringResource(R.string.screen_join_room_loading_alert_title), content = stringResource(CommonStrings.error_network_or_server_issue), - onRetry = { state.eventSink(JoinRoomEvents.RetryFetchingContent) }, + onRetry = { state.eventSink(JoinRoomEvent.RetryFetchingContent) }, onDismiss = { - state.eventSink(JoinRoomEvents.DismissErrorAndHideContent) + state.eventSink(JoinRoomEvent.DismissErrorAndHideContent) onBackClick() } ) @@ -166,7 +166,7 @@ fun JoinRoomView( errorTitle = { stringResource(CommonStrings.common_something_went_wrong) }, errorMessage = { stringResource(CommonStrings.error_network_or_server_issue) }, onSuccess = { onJoinSuccess() }, - onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) }, + onErrorDismiss = { state.eventSink(JoinRoomEvent.ClearActionStates) }, ) } AsyncActionView( @@ -174,19 +174,19 @@ fun JoinRoomView( errorTitle = { stringResource(CommonStrings.common_something_went_wrong) }, errorMessage = { stringResource(CommonStrings.error_network_or_server_issue) }, onSuccess = { onKnockSuccess() }, - onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) }, + onErrorDismiss = { state.eventSink(JoinRoomEvent.ClearActionStates) }, ) AsyncActionView( async = state.forgetAction, errorTitle = { stringResource(CommonStrings.common_something_went_wrong) }, errorMessage = { stringResource(CommonStrings.error_network_or_server_issue) }, onSuccess = { onForgetSuccess() }, - onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) }, + onErrorDismiss = { state.eventSink(JoinRoomEvent.ClearActionStates) }, ) AsyncActionView( async = state.cancelKnockAction, onSuccess = { onCancelKnockSuccess() }, - onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) }, + onErrorDismiss = { state.eventSink(JoinRoomEvent.ClearActionStates) }, errorTitle = { stringResource(CommonStrings.common_something_went_wrong) }, errorMessage = { stringResource(CommonStrings.error_network_or_server_issue) }, confirmationDialog = { @@ -195,8 +195,8 @@ fun JoinRoomView( title = stringResource(R.string.screen_join_room_cancel_knock_alert_title), submitText = stringResource(R.string.screen_join_room_cancel_knock_alert_confirmation), cancelText = stringResource(CommonStrings.action_no), - onSubmitClick = { state.eventSink(JoinRoomEvents.CancelKnock(requiresConfirmation = false)) }, - onDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) }, + onSubmitClick = { state.eventSink(JoinRoomEvent.CancelKnock(requiresConfirmation = false)) }, + onDismiss = { state.eventSink(JoinRoomEvent.ClearActionStates) }, ) }, ) diff --git a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt index a73a983034..3c104e47ae 100644 --- a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt +++ b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt @@ -388,8 +388,8 @@ class JoinRoomPresenterTest { skipItems(1) awaitItem().also { state -> - state.eventSink(JoinRoomEvents.AcceptInvite(inviteData)) - state.eventSink(JoinRoomEvents.DeclineInvite(inviteData, false)) + state.eventSink(JoinRoomEvent.AcceptInvite(inviteData)) + state.eventSink(JoinRoomEvent.DeclineInvite(inviteData, false)) assert(eventSinkRecorder) .isCalledExactly(2) @@ -422,7 +422,7 @@ class JoinRoomPresenterTest { presenter.test { skipItems(1) awaitItem().also { state -> - state.eventSink(JoinRoomEvents.JoinRoom) + state.eventSink(JoinRoomEvent.JoinRoom) } awaitItem().also { state -> assertThat(state.joinAction).isEqualTo(AsyncAction.Loading) @@ -453,14 +453,14 @@ class JoinRoomPresenterTest { presenter.test { skipItems(1) awaitItem().also { state -> - state.eventSink(JoinRoomEvents.JoinRoom) + state.eventSink(JoinRoomEvent.JoinRoom) } awaitItem().also { state -> assertThat(state.joinAction).isEqualTo(AsyncAction.Loading) } awaitItem().also { state -> assertThat(state.joinAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) - state.eventSink(JoinRoomEvents.ClearActionStates) + state.eventSink(JoinRoomEvent.ClearActionStates) } awaitItem().also { state -> assertThat(state.joinAction).isEqualTo(AsyncAction.Uninitialized) @@ -480,7 +480,7 @@ class JoinRoomPresenterTest { presenter.test { skipItems(1) awaitItem().also { state -> - state.eventSink(JoinRoomEvents.JoinRoom) + state.eventSink(JoinRoomEvent.JoinRoom) } awaitItem().also { state -> assertThat(state.joinAction).isEqualTo(AsyncAction.Loading) @@ -683,17 +683,17 @@ class JoinRoomPresenterTest { presenter.test { skipItems(1) awaitItem().also { state -> - state.eventSink(JoinRoomEvents.UpdateKnockMessage(knockMessage)) + state.eventSink(JoinRoomEvent.UpdateKnockMessage(knockMessage)) } awaitItem().also { state -> - state.eventSink(JoinRoomEvents.KnockRoom) + state.eventSink(JoinRoomEvent.KnockRoom) } assertThat(awaitItem().knockAction).isEqualTo(AsyncAction.Loading) awaitItem().also { state -> assertThat(state.knockAction).isEqualTo(AsyncAction.Success(Unit)) fakeKnockRoom.lambda = knockRoomFailure - state.eventSink(JoinRoomEvents.KnockRoom) + state.eventSink(JoinRoomEvent.KnockRoom) } assertThat(awaitItem().knockAction).isEqualTo(AsyncAction.Loading) @@ -731,17 +731,17 @@ class JoinRoomPresenterTest { presenter.test { skipItems(1) awaitItem().also { state -> - state.eventSink(JoinRoomEvents.CancelKnock(true)) + state.eventSink(JoinRoomEvent.CancelKnock(true)) } awaitItem().also { state -> assertThat(state.cancelKnockAction).isEqualTo(AsyncAction.ConfirmingNoParams) - state.eventSink(JoinRoomEvents.CancelKnock(false)) + state.eventSink(JoinRoomEvent.CancelKnock(false)) } assertThat(awaitItem().cancelKnockAction).isEqualTo(AsyncAction.Loading) awaitItem().also { state -> assertThat(state.cancelKnockAction).isEqualTo(AsyncAction.Success(Unit)) cancelKnockRoom.lambda = cancelKnockRoomFailure - state.eventSink(JoinRoomEvents.CancelKnock(false)) + state.eventSink(JoinRoomEvent.CancelKnock(false)) } assertThat(awaitItem().cancelKnockAction).isEqualTo(AsyncAction.Loading) awaitItem().also { state -> @@ -778,14 +778,14 @@ class JoinRoomPresenterTest { presenter.test { skipItems(1) awaitItem().also { state -> - state.eventSink(JoinRoomEvents.ForgetRoom) + state.eventSink(JoinRoomEvent.ForgetRoom) } assertThat(awaitItem().forgetAction).isEqualTo(AsyncAction.Loading) awaitItem().also { state -> assertThat(state.forgetAction).isEqualTo(AsyncAction.Success(Unit)) fakeForgetRoom.lambda = forgetRoomFailure - state.eventSink(JoinRoomEvents.ForgetRoom) + state.eventSink(JoinRoomEvent.ForgetRoom) } assertThat(awaitItem().forgetAction).isEqualTo(AsyncAction.Loading) diff --git a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomViewTest.kt b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomViewTest.kt index 0c510cacdb..c74e81010d 100644 --- a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomViewTest.kt +++ b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomViewTest.kt @@ -34,7 +34,7 @@ import org.junit.Test class JoinRoomViewTest : RobolectricTest() { @Test fun `clicking on back invoke the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setJoinRoomView( aJoinRoomState( @@ -48,7 +48,7 @@ class JoinRoomViewTest : RobolectricTest() { @Test fun `clicking on Join room on CanJoin room emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin), @@ -56,12 +56,12 @@ class JoinRoomViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_join_room_join_action) - eventsRecorder.assertSingle(JoinRoomEvents.JoinRoom) + eventsRecorder.assertSingle(JoinRoomEvent.JoinRoom) } @Test fun `clicking on Knock room on CanKnock room emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanKnock), @@ -70,12 +70,12 @@ class JoinRoomViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_join_room_knock_action) - eventsRecorder.assertSingle(JoinRoomEvents.KnockRoom) + eventsRecorder.assertSingle(JoinRoomEvent.KnockRoom) } @Test fun `clicking on closing Knock error emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanKnock), @@ -84,12 +84,12 @@ class JoinRoomViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_ok) - eventsRecorder.assertSingle(JoinRoomEvents.ClearActionStates) + eventsRecorder.assertSingle(JoinRoomEvent.ClearActionStates) } @Test fun `clicking on cancel knock request emit the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsKnocked), @@ -97,12 +97,12 @@ class JoinRoomViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_join_room_cancel_knock_action) - eventsRecorder.assertSingle(JoinRoomEvents.CancelKnock(true)) + eventsRecorder.assertSingle(JoinRoomEvent.CancelKnock(true)) } @Test fun `clicking on closing Cancel Knock error emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsKnocked), @@ -111,12 +111,12 @@ class JoinRoomViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_ok) - eventsRecorder.assertSingle(JoinRoomEvents.ClearActionStates) + eventsRecorder.assertSingle(JoinRoomEvent.ClearActionStates) } @Test fun `clicking on closing Join error emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanKnock), @@ -125,12 +125,12 @@ class JoinRoomViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_ok) - eventsRecorder.assertSingle(JoinRoomEvents.ClearActionStates) + eventsRecorder.assertSingle(JoinRoomEvent.ClearActionStates) } @Test fun `when joining room is successful, the expected callback is invoked`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setJoinRoomView( aJoinRoomState( @@ -144,7 +144,7 @@ class JoinRoomViewTest : RobolectricTest() { @Test fun `clicking on Accept when JoinAuthorisationStatus is IsInvited emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val inviteData = anInviteData() setJoinRoomView( aJoinRoomState( @@ -153,12 +153,12 @@ class JoinRoomViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_accept) - eventsRecorder.assertSingle(JoinRoomEvents.AcceptInvite(inviteData)) + eventsRecorder.assertSingle(JoinRoomEvent.AcceptInvite(inviteData)) } @Test fun `clicking on Decline when JoinAuthorisationStatus is IsInvited emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val inviteData = anInviteData() setJoinRoomView( aJoinRoomState( @@ -167,13 +167,13 @@ class JoinRoomViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_decline) - eventsRecorder.assertSingle(JoinRoomEvents.DeclineInvite(inviteData, false)) + eventsRecorder.assertSingle(JoinRoomEvent.DeclineInvite(inviteData, false)) } @Test fun `clicking on Decline and block when JoinAuthorisationStatus is IsInvited and can report room, the expected callback is invoked`() { runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) val inviteData = anInviteData() val joinRoomState = aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(inviteData, aRoomMember().toInviteSender())), @@ -192,7 +192,7 @@ class JoinRoomViewTest : RobolectricTest() { @Test fun `clicking on Decline and block when JoinAuthorisationStatus is IsInvited and cant report room, emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val inviteData = anInviteData() val joinRoomState = aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(inviteData, aRoomMember().toInviteSender())), @@ -201,12 +201,12 @@ class JoinRoomViewTest : RobolectricTest() { ) setJoinRoomView(state = joinRoomState) clickOn(R.string.screen_join_room_decline_and_block_button_title) - eventsRecorder.assertSingle(JoinRoomEvents.DeclineInvite(inviteData, true)) + eventsRecorder.assertSingle(JoinRoomEvent.DeclineInvite(inviteData, true)) } @Test fun `clicking on Retry when an error occurs emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setJoinRoomView( aJoinRoomState( contentState = aFailureContentState(), @@ -214,12 +214,12 @@ class JoinRoomViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_retry) - eventsRecorder.assertSingle(JoinRoomEvents.RetryFetchingContent) + eventsRecorder.assertSingle(JoinRoomEvent.RetryFetchingContent) } @Test fun `clicking on ok when user is unauthorized the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setJoinRoomView( aJoinRoomState( @@ -235,7 +235,7 @@ class JoinRoomViewTest : RobolectricTest() { @Test fun `clicking on forget when user is banned invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsBanned(null, null)), @@ -243,7 +243,7 @@ class JoinRoomViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_join_room_forget_action) - eventsRecorder.assertSingle(JoinRoomEvents.ForgetRoom) + eventsRecorder.assertSingle(JoinRoomEvent.ForgetRoom) } } From 7da820464fdb19d1512f3bba160faeb574181acb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 09:58:15 +0200 Subject: [PATCH 025/112] `KnockRequestsBannerEvents` -> `KnockRequestsBannerEvent` --- ...tsBannerEvents.kt => KnockRequestsBannerEvent.kt} | 6 +++--- .../impl/banner/KnockRequestsBannerPresenter.kt | 6 +++--- .../impl/banner/KnockRequestsBannerState.kt | 2 +- .../banner/KnockRequestsBannerStatePreviewParam.kt | 2 +- .../impl/banner/KnockRequestsBannerView.kt | 4 ++-- .../impl/banner/KnockRequestsBannerPresenterTest.kt | 6 +++--- .../impl/banner/KnockRequestsBannerViewTest.kt | 12 ++++++------ 7 files changed, 19 insertions(+), 19 deletions(-) rename features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/{KnockRequestsBannerEvents.kt => KnockRequestsBannerEvent.kt} (65%) diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerEvents.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerEvent.kt similarity index 65% rename from features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerEvents.kt rename to features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerEvent.kt index 740982b612..b8cd9345c2 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerEvents.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerEvent.kt @@ -8,7 +8,7 @@ package io.element.android.features.knockrequests.impl.banner -sealed interface KnockRequestsBannerEvents { - data object AcceptSingleRequest : KnockRequestsBannerEvents - data object Dismiss : KnockRequestsBannerEvents +sealed interface KnockRequestsBannerEvent { + data object AcceptSingleRequest : KnockRequestsBannerEvent + data object Dismiss : KnockRequestsBannerEvent } diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerPresenter.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerPresenter.kt index f340d597b1..a96b6d3de3 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerPresenter.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerPresenter.kt @@ -53,15 +53,15 @@ class KnockRequestsBannerPresenter( } } - fun handleEvent(event: KnockRequestsBannerEvents) { + fun handleEvent(event: KnockRequestsBannerEvent) { when (event) { - is KnockRequestsBannerEvents.AcceptSingleRequest -> { + is KnockRequestsBannerEvent.AcceptSingleRequest -> { sessionCoroutineScope.acceptSingleKnockRequest( knockRequests = knockRequests, displayAcceptError = showAcceptError, ) } - is KnockRequestsBannerEvents.Dismiss -> { + is KnockRequestsBannerEvent.Dismiss -> { sessionCoroutineScope.launch { knockRequestsService.markAllKnockRequestsAsSeen() } diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerState.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerState.kt index e9291b501a..2b6532c355 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerState.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerState.kt @@ -20,7 +20,7 @@ data class KnockRequestsBannerState( val knockRequests: ImmutableList, val displayAcceptError: Boolean, val canAccept: Boolean, - val eventSink: (KnockRequestsBannerEvents) -> Unit, + val eventSink: (KnockRequestsBannerEvent) -> Unit, ) { val subtitle = knockRequests.singleOrNull()?.userId?.value val reason = knockRequests.singleOrNull()?.reason diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStatePreviewParam.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStatePreviewParam.kt index fb05ae1fe0..8df5e08985 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStatePreviewParam.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStatePreviewParam.kt @@ -64,7 +64,7 @@ fun aKnockRequestsBannerState( displayAcceptError: Boolean = false, canAccept: Boolean = true, isVisible: Boolean = true, - eventSink: (KnockRequestsBannerEvents) -> Unit = {} + eventSink: (KnockRequestsBannerEvent) -> Unit = {} ) = KnockRequestsBannerState( knockRequests = knockRequests.toImmutableList(), displayAcceptError = displayAcceptError, diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerView.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerView.kt index 037ec16d00..329e7a2a56 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerView.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerView.kt @@ -109,11 +109,11 @@ private fun KnockRequestsBannerContent( modifier: Modifier = Modifier, ) { fun onDismissClick() { - state.eventSink(KnockRequestsBannerEvents.Dismiss) + state.eventSink(KnockRequestsBannerEvent.Dismiss) } fun onAcceptClick() { - state.eventSink(KnockRequestsBannerEvents.AcceptSingleRequest) + state.eventSink(KnockRequestsBannerEvent.AcceptSingleRequest) } Column( diff --git a/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerPresenterTest.kt b/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerPresenterTest.kt index 1595fcdbd9..2c1a185ba2 100644 --- a/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerPresenterTest.kt +++ b/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerPresenterTest.kt @@ -146,7 +146,7 @@ import org.junit.Test presenter.test { skipItems(2) awaitItem().also { state -> - state.eventSink(KnockRequestsBannerEvents.AcceptSingleRequest) + state.eventSink(KnockRequestsBannerEvent.AcceptSingleRequest) } awaitItem().also { state -> assertThat(state.isVisible).isFalse() @@ -182,7 +182,7 @@ import org.junit.Test skipItems(2) awaitItem().also { state -> assertThat(state.knockRequests).hasSize(1) - state.eventSink(KnockRequestsBannerEvents.AcceptSingleRequest) + state.eventSink(KnockRequestsBannerEvent.AcceptSingleRequest) } awaitItem().also { state -> assertThat(state.isVisible).isFalse() @@ -206,7 +206,7 @@ import org.junit.Test presenter.test { skipItems(2) awaitItem().also { state -> - state.eventSink(KnockRequestsBannerEvents.Dismiss) + state.eventSink(KnockRequestsBannerEvent.Dismiss) } advanceUntilIdle() assert(markAsSeenLambda).isCalledExactly(3) diff --git a/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerViewTest.kt b/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerViewTest.kt index f55ccb0cef..441b782a4a 100644 --- a/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerViewTest.kt +++ b/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerViewTest.kt @@ -29,7 +29,7 @@ import org.junit.Test class KnockRequestsBannerViewTest : RobolectricTest() { @Test fun `clicking on view on single request invoke the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setKnockRequestsBannerView( state = aKnockRequestsBannerState( @@ -43,7 +43,7 @@ class KnockRequestsBannerViewTest : RobolectricTest() { @Test fun `clicking on view all when multiple requests invoke the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setKnockRequestsBannerView( state = aKnockRequestsBannerState( @@ -62,19 +62,19 @@ class KnockRequestsBannerViewTest : RobolectricTest() { @Test fun `clicking on accept on a single request emit the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setKnockRequestsBannerView( state = aKnockRequestsBannerState( eventSink = eventsRecorder, ), ) clickOn(CommonStrings.action_accept) - eventsRecorder.assertSingle(KnockRequestsBannerEvents.AcceptSingleRequest) + eventsRecorder.assertSingle(KnockRequestsBannerEvent.AcceptSingleRequest) } @Test fun `clicking on dismiss emit the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setKnockRequestsBannerView( state = aKnockRequestsBannerState( eventSink = eventsRecorder, @@ -82,7 +82,7 @@ class KnockRequestsBannerViewTest : RobolectricTest() { ) val close = activity!!.getString(CommonStrings.action_close) onNodeWithContentDescription(close).performClick() - eventsRecorder.assertSingle(KnockRequestsBannerEvents.Dismiss) + eventsRecorder.assertSingle(KnockRequestsBannerEvent.Dismiss) } } From ee30d6aa418d01cc5ff211fc0163f2bd8027bf31 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 09:58:53 +0200 Subject: [PATCH 026/112] `KnockRequestsListEvents` -> `KnockRequestsListEvent` --- ...istEvents.kt => KnockRequestsListEvent.kt} | 16 ++++----- .../impl/list/KnockRequestsListPresenter.kt | 16 ++++----- .../impl/list/KnockRequestsListState.kt | 2 +- .../KnockRequestsListStatePreviewParam.kt | 2 +- .../impl/list/KnockRequestsListView.kt | 14 ++++---- .../list/KnockRequestsListPresenterTest.kt | 34 +++++++++---------- .../impl/list/KnockRequestsListViewTest.kt | 30 ++++++++-------- 7 files changed, 57 insertions(+), 57 deletions(-) rename features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/{KnockRequestsListEvents.kt => KnockRequestsListEvent.kt} (61%) diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListEvents.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListEvent.kt similarity index 61% rename from features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListEvents.kt rename to features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListEvent.kt index f1350b1669..6b9a6ac1a8 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListEvents.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListEvent.kt @@ -10,12 +10,12 @@ package io.element.android.features.knockrequests.impl.list import io.element.android.features.knockrequests.impl.data.KnockRequestPresentable -sealed interface KnockRequestsListEvents { - data class Accept(val knockRequest: KnockRequestPresentable) : KnockRequestsListEvents - data class Decline(val knockRequest: KnockRequestPresentable) : KnockRequestsListEvents - data class DeclineAndBan(val knockRequest: KnockRequestPresentable) : KnockRequestsListEvents - data object AcceptAll : KnockRequestsListEvents - data object ResetCurrentAction : KnockRequestsListEvents - data object RetryCurrentAction : KnockRequestsListEvents - data object ConfirmCurrentAction : KnockRequestsListEvents +sealed interface KnockRequestsListEvent { + data class Accept(val knockRequest: KnockRequestPresentable) : KnockRequestsListEvent + data class Decline(val knockRequest: KnockRequestPresentable) : KnockRequestsListEvent + data class DeclineAndBan(val knockRequest: KnockRequestPresentable) : KnockRequestsListEvent + data object AcceptAll : KnockRequestsListEvent + data object ResetCurrentAction : KnockRequestsListEvent + data object RetryCurrentAction : KnockRequestsListEvent + data object ConfirmCurrentAction : KnockRequestsListEvent } diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListPresenter.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListPresenter.kt index a56dc40731..76b562d8b3 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListPresenter.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListPresenter.kt @@ -39,28 +39,28 @@ class KnockRequestsListPresenter( val coroutineScope = rememberCoroutineScope() - fun handleEvent(event: KnockRequestsListEvents) { + fun handleEvent(event: KnockRequestsListEvent) { when (event) { - KnockRequestsListEvents.AcceptAll -> { + KnockRequestsListEvent.AcceptAll -> { currentAction = KnockRequestsAction.AcceptAll } - is KnockRequestsListEvents.Accept -> { + is KnockRequestsListEvent.Accept -> { currentAction = KnockRequestsAction.Accept(event.knockRequest) } - is KnockRequestsListEvents.Decline -> { + is KnockRequestsListEvent.Decline -> { currentAction = KnockRequestsAction.Decline(event.knockRequest) } - is KnockRequestsListEvents.DeclineAndBan -> { + is KnockRequestsListEvent.DeclineAndBan -> { currentAction = KnockRequestsAction.DeclineAndBan(event.knockRequest) } - KnockRequestsListEvents.ResetCurrentAction -> { + KnockRequestsListEvent.ResetCurrentAction -> { asyncAction.value = AsyncAction.Uninitialized currentAction = KnockRequestsAction.None } - KnockRequestsListEvents.RetryCurrentAction -> { + KnockRequestsListEvent.RetryCurrentAction -> { coroutineScope.executeAction(currentAction, asyncAction, isActionConfirmed = true) } - KnockRequestsListEvents.ConfirmCurrentAction -> { + KnockRequestsListEvent.ConfirmCurrentAction -> { coroutineScope.executeAction(currentAction, asyncAction, isActionConfirmed = true) } } diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListState.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListState.kt index ae770a297d..0a0e576eae 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListState.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListState.kt @@ -20,7 +20,7 @@ data class KnockRequestsListState( val currentAction: KnockRequestsAction, val asyncAction: AsyncAction, val permissions: KnockRequestPermissions, - val eventSink: (KnockRequestsListEvents) -> Unit, + val eventSink: (KnockRequestsListEvent) -> Unit, ) { val canAcceptAll = permissions.canAccept && knockRequests is AsyncData.Success && knockRequests.data.size > 1 } diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListStatePreviewParam.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListStatePreviewParam.kt index b51223a5e9..691d874af6 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListStatePreviewParam.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListStatePreviewParam.kt @@ -140,7 +140,7 @@ fun aKnockRequestsListState( canDecline = true, canBan = true, ), - eventSink: (KnockRequestsListEvents) -> Unit = {}, + eventSink: (KnockRequestsListEvent) -> Unit = {}, ) = KnockRequestsListState( knockRequests = knockRequests, currentAction = currentAction, diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListView.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListView.kt index d750927f74..52b11bc8c4 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListView.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListView.kt @@ -108,15 +108,15 @@ private fun KnockRequestsListContent( modifier: Modifier = Modifier, ) { fun onAcceptClick(knockRequest: KnockRequestPresentable) { - state.eventSink(KnockRequestsListEvents.Accept(knockRequest)) + state.eventSink(KnockRequestsListEvent.Accept(knockRequest)) } fun onDeclineClick(knockRequest: KnockRequestPresentable) { - state.eventSink(KnockRequestsListEvents.Decline(knockRequest)) + state.eventSink(KnockRequestsListEvent.Decline(knockRequest)) } fun onBanClick(knockRequest: KnockRequestPresentable) { - state.eventSink(KnockRequestsListEvents.DeclineAndBan(knockRequest)) + state.eventSink(KnockRequestsListEvent.DeclineAndBan(knockRequest)) } var bottomPaddingInPixels by remember { mutableIntStateOf(0) } @@ -160,19 +160,19 @@ private fun KnockRequestsListContent( currentAction = state.currentAction, asyncAction = state.asyncAction, onConfirm = { - state.eventSink(KnockRequestsListEvents.ConfirmCurrentAction) + state.eventSink(KnockRequestsListEvent.ConfirmCurrentAction) }, onRetry = { - state.eventSink(KnockRequestsListEvents.RetryCurrentAction) + state.eventSink(KnockRequestsListEvent.RetryCurrentAction) }, onDismiss = { - state.eventSink(KnockRequestsListEvents.ResetCurrentAction) + state.eventSink(KnockRequestsListEvent.ResetCurrentAction) }, ) if (state.canAcceptAll) { KnockRequestsAcceptAll( onClick = { - state.eventSink(KnockRequestsListEvents.AcceptAll) + state.eventSink(KnockRequestsListEvent.AcceptAll) }, onHeightChange = { height -> bottomPaddingInPixels = height diff --git a/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListPresenterTest.kt b/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListPresenterTest.kt index 209e67cadf..dc04889d32 100644 --- a/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListPresenterTest.kt +++ b/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListPresenterTest.kt @@ -65,7 +65,7 @@ class KnockRequestsListPresenterTest { skipItems(2) awaitItem().also { state -> val knockRequestPresentable = state.knockRequests.dataOrNull()?.first()!! - state.eventSink(KnockRequestsListEvents.Accept(knockRequestPresentable)) + state.eventSink(KnockRequestsListEvent.Accept(knockRequestPresentable)) } skipItems(1) awaitItem().also { state -> @@ -75,7 +75,7 @@ class KnockRequestsListPresenterTest { } awaitItem().also { state -> assertThat(state.asyncAction).isInstanceOf(AsyncAction.Success::class.java) - state.eventSink(KnockRequestsListEvents.ResetCurrentAction) + state.eventSink(KnockRequestsListEvent.ResetCurrentAction) } skipItems(2) awaitItem().also { state -> @@ -99,7 +99,7 @@ class KnockRequestsListPresenterTest { skipItems(2) awaitItem().also { state -> val knockRequestPresentable = state.knockRequests.dataOrNull()?.first()!! - state.eventSink(KnockRequestsListEvents.Accept(knockRequestPresentable)) + state.eventSink(KnockRequestsListEvent.Accept(knockRequestPresentable)) } skipItems(1) awaitItem().also { state -> @@ -109,14 +109,14 @@ class KnockRequestsListPresenterTest { } awaitItem().also { state -> assertThat(state.asyncAction).isInstanceOf(AsyncAction.Failure::class.java) - state.eventSink(KnockRequestsListEvents.RetryCurrentAction) + state.eventSink(KnockRequestsListEvent.RetryCurrentAction) } awaitItem().also { state -> assertThat(state.asyncAction).isInstanceOf(AsyncAction.Loading::class.java) } awaitItem().also { state -> assertThat(state.asyncAction).isInstanceOf(AsyncAction.Failure::class.java) - state.eventSink(KnockRequestsListEvents.ResetCurrentAction) + state.eventSink(KnockRequestsListEvent.ResetCurrentAction) } skipItems(1) awaitItem().also { state -> @@ -140,21 +140,21 @@ class KnockRequestsListPresenterTest { skipItems(2) awaitItem().also { state -> val knockRequestPresentable = state.knockRequests.dataOrNull()?.first()!! - state.eventSink(KnockRequestsListEvents.Decline(knockRequestPresentable)) + state.eventSink(KnockRequestsListEvent.Decline(knockRequestPresentable)) } skipItems(1) awaitItem().also { state -> val knockRequestPresentable = state.knockRequests.dataOrNull()?.first()!! assertThat(state.currentAction).isEqualTo(KnockRequestsAction.Decline(knockRequestPresentable)) assertThat(state.asyncAction).isInstanceOf(AsyncAction.ConfirmingNoParams::class.java) - state.eventSink(KnockRequestsListEvents.ConfirmCurrentAction) + state.eventSink(KnockRequestsListEvent.ConfirmCurrentAction) } awaitItem().also { state -> assertThat(state.asyncAction).isInstanceOf(AsyncAction.Loading::class.java) } awaitItem().also { state -> assertThat(state.asyncAction).isInstanceOf(AsyncAction.Success::class.java) - state.eventSink(KnockRequestsListEvents.ResetCurrentAction) + state.eventSink(KnockRequestsListEvent.ResetCurrentAction) } skipItems(2) awaitItem().also { state -> @@ -178,21 +178,21 @@ class KnockRequestsListPresenterTest { skipItems(2) awaitItem().also { state -> val knockRequestPresentable = state.knockRequests.dataOrNull()?.first()!! - state.eventSink(KnockRequestsListEvents.DeclineAndBan(knockRequestPresentable)) + state.eventSink(KnockRequestsListEvent.DeclineAndBan(knockRequestPresentable)) } skipItems(1) awaitItem().also { state -> val knockRequestPresentable = state.knockRequests.dataOrNull()?.first()!! assertThat(state.currentAction).isEqualTo(KnockRequestsAction.DeclineAndBan(knockRequestPresentable)) assertThat(state.asyncAction).isInstanceOf(AsyncAction.ConfirmingNoParams::class.java) - state.eventSink(KnockRequestsListEvents.ConfirmCurrentAction) + state.eventSink(KnockRequestsListEvent.ConfirmCurrentAction) } awaitItem().also { state -> assertThat(state.asyncAction).isInstanceOf(AsyncAction.Loading::class.java) } awaitItem().also { state -> assertThat(state.asyncAction).isInstanceOf(AsyncAction.Success::class.java) - state.eventSink(KnockRequestsListEvents.ResetCurrentAction) + state.eventSink(KnockRequestsListEvent.ResetCurrentAction) } skipItems(2) awaitItem().also { state -> @@ -220,20 +220,20 @@ class KnockRequestsListPresenterTest { skipItems(2) awaitItem().also { state -> assertThat(state.canAcceptAll).isTrue() - state.eventSink(KnockRequestsListEvents.AcceptAll) + state.eventSink(KnockRequestsListEvent.AcceptAll) } skipItems(1) awaitItem().also { state -> assertThat(state.currentAction).isEqualTo(KnockRequestsAction.AcceptAll) assertThat(state.asyncAction).isInstanceOf(AsyncAction.ConfirmingNoParams::class.java) - state.eventSink(KnockRequestsListEvents.ConfirmCurrentAction) + state.eventSink(KnockRequestsListEvent.ConfirmCurrentAction) } awaitItem().also { state -> assertThat(state.asyncAction).isInstanceOf(AsyncAction.Loading::class.java) } awaitItem().also { state -> assertThat(state.asyncAction).isInstanceOf(AsyncAction.Success::class.java) - state.eventSink(KnockRequestsListEvents.ResetCurrentAction) + state.eventSink(KnockRequestsListEvent.ResetCurrentAction) } skipItems(2) awaitItem().also { state -> @@ -262,20 +262,20 @@ class KnockRequestsListPresenterTest { skipItems(2) awaitItem().also { state -> assertThat(state.canAcceptAll).isTrue() - state.eventSink(KnockRequestsListEvents.AcceptAll) + state.eventSink(KnockRequestsListEvent.AcceptAll) } skipItems(1) awaitItem().also { state -> assertThat(state.currentAction).isEqualTo(KnockRequestsAction.AcceptAll) assertThat(state.asyncAction).isInstanceOf(AsyncAction.ConfirmingNoParams::class.java) - state.eventSink(KnockRequestsListEvents.ConfirmCurrentAction) + state.eventSink(KnockRequestsListEvent.ConfirmCurrentAction) } awaitItem().also { state -> assertThat(state.asyncAction).isInstanceOf(AsyncAction.Loading::class.java) } awaitItem().also { state -> assertThat(state.asyncAction).isInstanceOf(AsyncAction.Failure::class.java) - state.eventSink(KnockRequestsListEvents.ResetCurrentAction) + state.eventSink(KnockRequestsListEvent.ResetCurrentAction) } skipItems(2) awaitItem().also { state -> diff --git a/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListViewTest.kt b/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListViewTest.kt index 1fbcb67b21..c1caaddb1e 100644 --- a/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListViewTest.kt +++ b/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListViewTest.kt @@ -31,7 +31,7 @@ import org.junit.Test class KnockRequestsListViewTest : RobolectricTest() { @Test fun `clicking on back invoke the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setKnockRequestsListView( aKnockRequestsListState( @@ -45,7 +45,7 @@ class KnockRequestsListViewTest : RobolectricTest() { @Test fun `clicking on accept emit the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val knockRequest = aKnockRequestPresentable() setKnockRequestsListView( aKnockRequestsListState( @@ -54,12 +54,12 @@ class KnockRequestsListViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_accept) - eventsRecorder.assertSingle(KnockRequestsListEvents.Accept(knockRequest)) + eventsRecorder.assertSingle(KnockRequestsListEvent.Accept(knockRequest)) } @Test fun `clicking on decline emit the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val knockRequest = aKnockRequestPresentable() setKnockRequestsListView( aKnockRequestsListState( @@ -68,12 +68,12 @@ class KnockRequestsListViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_decline) - eventsRecorder.assertSingle(KnockRequestsListEvents.Decline(knockRequest)) + eventsRecorder.assertSingle(KnockRequestsListEvent.Decline(knockRequest)) } @Test fun `clicking on decline and ban emit the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val knockRequest = aKnockRequestPresentable() setKnockRequestsListView( aKnockRequestsListState( @@ -82,12 +82,12 @@ class KnockRequestsListViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_knock_requests_list_decline_and_ban_action_title) - eventsRecorder.assertSingle(KnockRequestsListEvents.DeclineAndBan(knockRequest)) + eventsRecorder.assertSingle(KnockRequestsListEvent.DeclineAndBan(knockRequest)) } @Test fun `clicking on accept all emit the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val knockRequests = persistentListOf(aKnockRequestPresentable(), aKnockRequestPresentable()) setKnockRequestsListView( aKnockRequestsListState( @@ -96,12 +96,12 @@ class KnockRequestsListViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_knock_requests_list_accept_all_button_title) - eventsRecorder.assertSingle(KnockRequestsListEvents.AcceptAll) + eventsRecorder.assertSingle(KnockRequestsListEvent.AcceptAll) } @Test fun `retry on async view retry emit the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val knockRequests = persistentListOf(aKnockRequestPresentable(), aKnockRequestPresentable()) setKnockRequestsListView( aKnockRequestsListState( @@ -112,12 +112,12 @@ class KnockRequestsListViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_retry) - eventsRecorder.assertSingle(KnockRequestsListEvents.RetryCurrentAction) + eventsRecorder.assertSingle(KnockRequestsListEvent.RetryCurrentAction) } @Test fun `canceling async view emit the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val knockRequests = persistentListOf(aKnockRequestPresentable(), aKnockRequestPresentable()) setKnockRequestsListView( aKnockRequestsListState( @@ -128,12 +128,12 @@ class KnockRequestsListViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_cancel) - eventsRecorder.assertSingle(KnockRequestsListEvents.ResetCurrentAction) + eventsRecorder.assertSingle(KnockRequestsListEvent.ResetCurrentAction) } @Test fun `confirming async view emit the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val knockRequests = persistentListOf(aKnockRequestPresentable(), aKnockRequestPresentable()) setKnockRequestsListView( aKnockRequestsListState( @@ -144,7 +144,7 @@ class KnockRequestsListViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_knock_requests_list_accept_all_alert_confirm_button_title) - eventsRecorder.assertSingle(KnockRequestsListEvents.ConfirmCurrentAction) + eventsRecorder.assertSingle(KnockRequestsListEvent.ConfirmCurrentAction) } } From e07099f11649c7488921c6081fdbc96f5649f2dc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 09:59:31 +0200 Subject: [PATCH 027/112] `PermissionsEvents` -> `PermissionsEvent` --- .../common/permissions/DefaultPermissionsPresenter.kt | 4 ++-- .../{PermissionsEvents.kt => PermissionsEvent.kt} | 4 ++-- .../location/impl/common/permissions/PermissionsState.kt | 2 +- .../location/impl/share/ShareLocationPresenter.kt | 8 ++++---- .../features/location/impl/show/ShowLocationPresenter.kt | 6 +++--- .../impl/common/permissions/FakePermissionsPresenter.kt | 4 ++-- .../location/impl/share/ShareLocationPresenterTest.kt | 6 +++--- .../location/impl/show/ShowLocationPresenterTest.kt | 6 +++--- 8 files changed, 20 insertions(+), 20 deletions(-) rename features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/{PermissionsEvents.kt => PermissionsEvent.kt} (77%) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/DefaultPermissionsPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/DefaultPermissionsPresenter.kt index 5aa7ae6f63..01fa8bca7e 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/DefaultPermissionsPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/DefaultPermissionsPresenter.kt @@ -41,9 +41,9 @@ class DefaultPermissionsPresenter( permissionsRequested = true } - fun handleEvent(event: PermissionsEvents) { + fun handleEvent(event: PermissionsEvent) { when (event) { - PermissionsEvents.RequestPermissions -> { + PermissionsEvent.RequestPermissions -> { multiplePermissionsState.launchMultiplePermissionRequest() } } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsEvents.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsEvent.kt similarity index 77% rename from features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsEvents.kt rename to features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsEvent.kt index a4b5ef1228..226909af5d 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsEvents.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsEvent.kt @@ -8,6 +8,6 @@ package io.element.android.features.location.impl.common.permissions -sealed interface PermissionsEvents { - data object RequestPermissions : PermissionsEvents +sealed interface PermissionsEvent { + data object RequestPermissions : PermissionsEvent } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsState.kt index 25ceb3ba29..a8dfd571e5 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsState.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsState.kt @@ -12,7 +12,7 @@ data class PermissionsState( val permissions: Permissions, val shouldShowRationale: Boolean, val permissionsAlreadyRequested: Boolean, - val eventSink: (PermissionsEvents) -> Unit, + val eventSink: (PermissionsEvent) -> Unit, ) { sealed interface Permissions { data object AllGranted : Permissions diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt index 55f1e088d2..ccdf598e93 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt @@ -29,7 +29,7 @@ import io.element.android.features.location.impl.common.MapDefaults import io.element.android.features.location.impl.common.SendLiveLocationPermissions import io.element.android.features.location.impl.common.actions.LocationActions import io.element.android.features.location.impl.common.checkLocationConstraints -import io.element.android.features.location.impl.common.permissions.PermissionsEvents +import io.element.android.features.location.impl.common.permissions.PermissionsEvent import io.element.android.features.location.impl.common.permissions.PermissionsPresenter import io.element.android.features.location.impl.common.permissions.PermissionsState import io.element.android.features.location.impl.common.sendLiveLocationPermissions @@ -111,7 +111,7 @@ class ShareLocationPresenter( sendLiveLocationPermissions = SendLiveLocationPermissions.GRANTED ) if (locationConstraints is LocationConstraintsCheck.PermissionShouldBeRequested) { - permissionsState.eventSink(PermissionsEvents.RequestPermissions) + permissionsState.eventSink(PermissionsEvent.RequestPermissions) } trackUserPosition = locationConstraints is LocationConstraintsCheck.Success dialogState = ShareLocationState.Dialog.Constraints(locationConstraints.toDialogState()) @@ -137,7 +137,7 @@ class ShareLocationPresenter( } else -> { if (locationConstraints is LocationConstraintsCheck.PermissionShouldBeRequested) { - permissionsState.eventSink(PermissionsEvents.RequestPermissions) + permissionsState.eventSink(PermissionsEvent.RequestPermissions) } dialogState = ShareLocationState.Dialog.Constraints(locationConstraints.toDialogState()) } @@ -195,7 +195,7 @@ class ShareLocationPresenter( } ShareLocationEvent.RequestPermissions -> { dialogState = ShareLocationState.Dialog.None - permissionsState.eventSink(PermissionsEvents.RequestPermissions) + permissionsState.eventSink(PermissionsEvent.RequestPermissions) } } } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt index bbec7b449f..412e96eb9c 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt @@ -32,7 +32,7 @@ import io.element.android.features.location.impl.common.MapDefaults import io.element.android.features.location.impl.common.SendLiveLocationPermissions import io.element.android.features.location.impl.common.actions.LocationActions import io.element.android.features.location.impl.common.checkLocationConstraints -import io.element.android.features.location.impl.common.permissions.PermissionsEvents +import io.element.android.features.location.impl.common.permissions.PermissionsEvent import io.element.android.features.location.impl.common.permissions.PermissionsPresenter import io.element.android.features.location.impl.common.permissions.PermissionsState import io.element.android.features.location.impl.common.toDialogState @@ -99,7 +99,7 @@ class ShowLocationPresenter( sendLiveLocationPermissions = SendLiveLocationPermissions.GRANTED ) if (locationConstraints is LocationConstraintsCheck.PermissionShouldBeRequested) { - permissionsState.eventSink(PermissionsEvents.RequestPermissions) + permissionsState.eventSink(PermissionsEvent.RequestPermissions) } isTrackMyLocation = locationConstraints is LocationConstraintsCheck.Success dialogState = locationConstraints.toDialogState() @@ -130,7 +130,7 @@ class ShowLocationPresenter( locationActions.openLocationSettings() dialogState = LocationConstraintsDialogState.None } - ShowLocationEvent.RequestPermissions -> permissionsState.eventSink(PermissionsEvents.RequestPermissions) + ShowLocationEvent.RequestPermissions -> permissionsState.eventSink(PermissionsEvent.RequestPermissions) ShowLocationEvent.StopLocationSharing -> coroutineScope.launch { liveLocationShareManager.stopShare(joinedRoom.roomId) } diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/FakePermissionsPresenter.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/FakePermissionsPresenter.kt index 9b8ff7a29a..c25cbc24fc 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/FakePermissionsPresenter.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/FakePermissionsPresenter.kt @@ -11,9 +11,9 @@ package io.element.android.features.location.impl.common.permissions import androidx.compose.runtime.Composable class FakePermissionsPresenter : PermissionsPresenter { - val events = mutableListOf() + val events = mutableListOf() - private fun handleEvent(event: PermissionsEvents) { + private fun handleEvent(event: PermissionsEvent) { events += event } diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt index d249fdaf69..7500d29616 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt @@ -26,7 +26,7 @@ import io.element.android.features.location.impl.aPermissionsState import io.element.android.features.location.impl.common.FakeUserLocationStateFactory import io.element.android.features.location.impl.common.actions.FakeLocationActions import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter -import io.element.android.features.location.impl.common.permissions.PermissionsEvents +import io.element.android.features.location.impl.common.permissions.PermissionsEvent import io.element.android.features.location.impl.common.permissions.PermissionsState import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState import io.element.android.features.location.impl.live.LiveLocationStore @@ -174,7 +174,7 @@ class ShareLocationPresenterTest { shareLocationPresenter.test { skipItems(2) cancelAndIgnoreRemainingEvents() - assertThat(fakePermissionsPresenter.events).contains(PermissionsEvents.RequestPermissions) + assertThat(fakePermissionsPresenter.events).contains(PermissionsEvent.RequestPermissions) } } @@ -298,7 +298,7 @@ class ShareLocationPresenterTest { // Wait for dialog to be dismissed awaitItem() - assertThat(fakePermissionsPresenter.events.last()).isEqualTo(PermissionsEvents.RequestPermissions) + assertThat(fakePermissionsPresenter.events.last()).isEqualTo(PermissionsEvent.RequestPermissions) cancelAndIgnoreRemainingEvents() } } diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt index 7d9b0d5e03..1fab0d98af 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt @@ -20,7 +20,7 @@ import io.element.android.features.location.impl.aPermissionsState import io.element.android.features.location.impl.common.FakeUserLocationStateFactory import io.element.android.features.location.impl.common.actions.FakeLocationActions import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter -import io.element.android.features.location.impl.common.permissions.PermissionsEvents +import io.element.android.features.location.impl.common.permissions.PermissionsEvent import io.element.android.features.location.impl.common.permissions.PermissionsState import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState import io.element.android.features.location.test.FakeActiveLiveLocationShareManager @@ -188,7 +188,7 @@ class ShowLocationPresenterTest { // Continue the dialog sends permission request to the permissions presenter trackLocationState.eventSink(ShowLocationEvent.RequestPermissions) - assertThat(fakePermissionsPresenter.events.last()).isEqualTo(PermissionsEvents.RequestPermissions) + assertThat(fakePermissionsPresenter.events.last()).isEqualTo(PermissionsEvent.RequestPermissions) } } @@ -208,7 +208,7 @@ class ShowLocationPresenterTest { initialState.eventSink(ShowLocationEvent.TrackMyLocation(true)) - assertThat(fakePermissionsPresenter.events).contains(PermissionsEvents.RequestPermissions) + assertThat(fakePermissionsPresenter.events).contains(PermissionsEvent.RequestPermissions) cancelAndIgnoreRemainingEvents() } } From 0ee7ed3a788f1196aa7d6153eb0568917547dd0b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:00:15 +0200 Subject: [PATCH 028/112] `ChangeServerEvents` -> `ChangeServerEvent` --- ...geServerEvents.kt => ChangeServerEvent.kt} | 10 +++++----- .../changeserver/ChangeServerPresenter.kt | 10 +++++----- .../impl/changeserver/ChangeServerState.kt | 2 +- .../impl/changeserver/ChangeServerView.kt | 20 +++++++++---------- .../ConfirmAccountProviderPresenter.kt | 6 +++--- .../changeserver/ChangeServerPresenterTest.kt | 20 +++++++++---------- 6 files changed, 34 insertions(+), 34 deletions(-) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/{ChangeServerEvents.kt => ChangeServerEvent.kt} (83%) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerEvents.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerEvent.kt similarity index 83% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerEvents.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerEvent.kt index 4f0d228dd2..f3fd02713f 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerEvents.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerEvent.kt @@ -10,9 +10,9 @@ package io.element.android.features.login.impl.changeserver import io.element.android.features.login.impl.accountprovider.AccountProvider -sealed interface ChangeServerEvents { - data class ChangeServer(val accountProvider: AccountProvider) : ChangeServerEvents - data object ClearError : ChangeServerEvents - data object RequestLocalNetworkPermission : ChangeServerEvents - data object DismissLocalNetworkPermission : ChangeServerEvents +sealed interface ChangeServerEvent { + data class ChangeServer(val accountProvider: AccountProvider) : ChangeServerEvent + data object ClearError : ChangeServerEvent + data object RequestLocalNetworkPermission : ChangeServerEvent + data object DismissLocalNetworkPermission : ChangeServerEvent } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt index b7b04beeb2..9106d6e1c4 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt @@ -42,12 +42,12 @@ class ChangeServerPresenter( onProceed = { provider -> changeServer(provider, changeServerAction) }, ) - fun handleEvent(event: ChangeServerEvents) { + fun handleEvent(event: ChangeServerEvent) { when (event) { - ChangeServerEvents.ClearError -> changeServerAction.value = AsyncData.Uninitialized - is ChangeServerEvents.ChangeServer -> gateState.submit(event.accountProvider) - ChangeServerEvents.DismissLocalNetworkPermission -> gateState.abort() - ChangeServerEvents.RequestLocalNetworkPermission -> gateState.requestPermission() + ChangeServerEvent.ClearError -> changeServerAction.value = AsyncData.Uninitialized + is ChangeServerEvent.ChangeServer -> gateState.submit(event.accountProvider) + ChangeServerEvent.DismissLocalNetworkPermission -> gateState.abort() + ChangeServerEvent.RequestLocalNetworkPermission -> gateState.requestPermission() } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerState.kt index 3b4f4d9dad..1b69551e94 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerState.kt @@ -17,5 +17,5 @@ data class ChangeServerState( // configuring (and re-networking) the homeserver a second time. val changeServerAction: AsyncData, val localNetworkPermissionDialog: LocalNetworkPermissionDialog, - val eventSink: (ChangeServerEvents) -> Unit + val eventSink: (ChangeServerEvent) -> Unit ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt index 8e726c7fe1..e629c6bce3 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt @@ -47,7 +47,7 @@ fun ChangeServerView( modifier = modifier, content = stringResource(R.string.screen_change_server_error_invalid_homeserver), onSubmit = { - eventSink.invoke(ChangeServerEvents.ClearError) + eventSink.invoke(ChangeServerEvent.ClearError) } ) ChangeServerError.UnsupportedServer -> @@ -55,7 +55,7 @@ fun ChangeServerView( modifier = modifier, content = stringResource(R.string.screen_login_error_unsupported_authentication), onSubmit = { - eventSink.invoke(ChangeServerEvents.ClearError) + eventSink.invoke(ChangeServerEvent.ClearError) } ) is ChangeServerError.Error -> { @@ -63,7 +63,7 @@ fun ChangeServerView( modifier = modifier, content = error.messageStr ?: stringResource(CommonStrings.error_unknown), onSubmit = { - eventSink.invoke(ChangeServerEvents.ClearError) + eventSink.invoke(ChangeServerEvent.ClearError) } ) } @@ -72,10 +72,10 @@ fun ChangeServerView( modifier = modifier, onLearnMoreClick = { onLearnMoreClick() - eventSink.invoke(ChangeServerEvents.ClearError) + eventSink.invoke(ChangeServerEvent.ClearError) }, onDismiss = { - eventSink.invoke(ChangeServerEvents.ClearError) + eventSink.invoke(ChangeServerEvent.ClearError) } ) } @@ -90,10 +90,10 @@ fun ChangeServerView( submitText = stringResource(R.string.screen_change_server_error_element_pro_required_action_android), onSubmitClick = { context.openGooglePlay(error.applicationId) - eventSink.invoke(ChangeServerEvents.ClearError) + eventSink.invoke(ChangeServerEvent.ClearError) }, onDismiss = { - eventSink.invoke(ChangeServerEvents.ClearError) + eventSink.invoke(ChangeServerEvent.ClearError) }, ) } @@ -106,7 +106,7 @@ fun ChangeServerView( error.unauthorisedAccountProviderTitle, ), onSubmit = { - eventSink.invoke(ChangeServerEvents.ClearError) + eventSink.invoke(ChangeServerEvent.ClearError) } ) } @@ -125,10 +125,10 @@ fun ChangeServerView( LocalNetworkPermissionDialogView( dialog = state.localNetworkPermissionDialog, onSubmit = { - eventSink.invoke(ChangeServerEvents.RequestLocalNetworkPermission) + eventSink.invoke(ChangeServerEvent.RequestLocalNetworkPermission) }, onDismiss = { - eventSink.invoke(ChangeServerEvents.DismissLocalNetworkPermission) + eventSink.invoke(ChangeServerEvent.DismissLocalNetworkPermission) } ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt index 5c66f8f446..9fae52ab0b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt @@ -24,7 +24,7 @@ import io.element.android.appconfig.AuthenticationConfig import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource -import io.element.android.features.login.impl.changeserver.ChangeServerEvents +import io.element.android.features.login.impl.changeserver.ChangeServerEvent import io.element.android.features.login.impl.changeserver.ChangeServerState import io.element.android.features.login.impl.login.LoginModeEvent import io.element.android.features.login.impl.login.LoginModeState @@ -134,12 +134,12 @@ class ConfirmAccountProviderPresenter( val accountProviderUrl = host.ensureProtocol() submittedAccountProvider = accountProviderUrl changeServerState.eventSink( - ChangeServerEvents.ChangeServer(AccountProvider(url = accountProviderUrl)) + ChangeServerEvent.ChangeServer(AccountProvider(url = accountProviderUrl)) ) } ConfirmAccountProviderEvents.ClearError -> { loginModeState.eventSink(LoginModeEvent.ClearError) - changeServerState.eventSink(ChangeServerEvents.ClearError) + changeServerState.eventSink(ChangeServerEvent.ClearError) } } } 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 ad997111e2..44a3bbd5cb 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 @@ -63,7 +63,7 @@ class ChangeServerPresenterTest { ).test { val initialState = awaitItem() assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) - initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) + initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) val loadingState = awaitItem() assertThat(loadingState.changeServerAction).isInstanceOf(AsyncData.Loading::class.java) val successState = awaitItem() @@ -87,13 +87,13 @@ class ChangeServerPresenterTest { ).test { val initialState = awaitItem() assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) - initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) + initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) val loadingState = awaitItem() assertThat(loadingState.changeServerAction).isInstanceOf(AsyncData.Loading::class.java) val failureState = awaitItem() assertThat(failureState.changeServerAction).isInstanceOf(AsyncData.Failure::class.java) // Clear error - failureState.eventSink.invoke(ChangeServerEvents.ClearError) + failureState.eventSink.invoke(ChangeServerEvent.ClearError) val finalState = awaitItem() assertThat(finalState.changeServerAction).isEqualTo(AsyncData.Uninitialized) } @@ -115,7 +115,7 @@ class ChangeServerPresenterTest { ).test { val initialState = awaitItem() assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) - initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) + initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) val loadingState = awaitItem() assertThat(loadingState.changeServerAction).isInstanceOf(AsyncData.Loading::class.java) val failureState = awaitItem() @@ -139,7 +139,7 @@ class ChangeServerPresenterTest { val initialState = awaitItem() assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) val anAccountProvider = AccountProvider(url = A_HOMESERVER_URL) - initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(anAccountProvider)) + initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(anAccountProvider)) val loadingState = awaitItem() assertThat(loadingState.changeServerAction).isInstanceOf(AsyncData.Loading::class.java) val failureState = awaitItem() @@ -167,7 +167,7 @@ class ChangeServerPresenterTest { val initialState = awaitItem() assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) val anAccountProvider = AccountProvider(url = A_HOMESERVER_URL) - initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(anAccountProvider)) + initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(anAccountProvider)) // Skip loading state skipItems(1) val failureState = awaitItem() @@ -194,7 +194,7 @@ class ChangeServerPresenterTest { ).test { val initialState = awaitItem() assertThat(initialState.localNetworkPermissionDialog).isEqualTo(LocalNetworkPermissionDialog.None) - initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) + initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) val promptState = expectMostRecentItem() // Dialog is shown, permission has not been requested yet, setHomeserver has not been called. assertThat(promptState.localNetworkPermissionDialog).isNotEqualTo(LocalNetworkPermissionDialog.None) @@ -213,10 +213,10 @@ class ChangeServerPresenterTest { permissionsPresenter = FakePermissionsPresenter(), ).test { val initialState = awaitItem() - initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) + initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) val promptState = expectMostRecentItem() assertThat(promptState.localNetworkPermissionDialog).isNotEqualTo(LocalNetworkPermissionDialog.None) - promptState.eventSink.invoke(ChangeServerEvents.DismissLocalNetworkPermission) + promptState.eventSink.invoke(ChangeServerEvent.DismissLocalNetworkPermission) val dismissedState = expectMostRecentItem() assertThat(dismissedState.localNetworkPermissionDialog).isEqualTo(LocalNetworkPermissionDialog.None) assertThat(dismissedState.changeServerAction).isEqualTo(AsyncData.Uninitialized) @@ -236,7 +236,7 @@ class ChangeServerPresenterTest { permissionsPresenter = permissionsPresenter, ).test { val initialState = awaitItem() - initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) + initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) val promptState = expectMostRecentItem() assertThat(promptState.localNetworkPermissionDialog).isNotEqualTo(LocalNetworkPermissionDialog.None) permissionsPresenter.setPermissionGranted() From 1fd102ffc37bf53f57048829e567be59c743b552 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:01:04 +0200 Subject: [PATCH 029/112] `ChooseAccountProviderEvents` -> `ChooseAccountProviderEvent` --- ...iderEvents.kt => ChooseAccountProviderEvent.kt} | 8 ++++---- .../ChooseAccountProviderPresenter.kt | 8 ++++---- .../ChooseAccountProviderState.kt | 2 +- .../ChooseAccountProviderStatePreviewParam.kt | 2 +- .../ChooseAccountProviderView.kt | 6 +++--- .../ChooseAccountProviderPresenterTest.kt | 14 +++++++------- .../ChooseAccountProviderViewTest.kt | 10 +++++----- 7 files changed, 25 insertions(+), 25 deletions(-) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/{ChooseAccountProviderEvents.kt => ChooseAccountProviderEvent.kt} (69%) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderEvents.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderEvent.kt similarity index 69% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderEvents.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderEvent.kt index f60cc3e9da..bb50f123cb 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderEvents.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderEvent.kt @@ -10,8 +10,8 @@ package io.element.android.features.login.impl.screens.chooseaccountprovider import io.element.android.features.login.impl.accountprovider.AccountProvider -sealed interface ChooseAccountProviderEvents { - data class SelectAccountProvider(val accountProvider: AccountProvider) : ChooseAccountProviderEvents - data object Continue : ChooseAccountProviderEvents - data object ClearError : ChooseAccountProviderEvents +sealed interface ChooseAccountProviderEvent { + data class SelectAccountProvider(val accountProvider: AccountProvider) : ChooseAccountProviderEvent + data object Continue : ChooseAccountProviderEvent + data object ClearError : ChooseAccountProviderEvent } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenter.kt index 92c3ff0fff..e2863a295a 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenter.kt @@ -34,9 +34,9 @@ class ChooseAccountProviderPresenter( val loginModeState = loginModePresenter.present() var selectedAccountProvider: AccountProvider? by remember { mutableStateOf(null) } - fun handleEvent(event: ChooseAccountProviderEvents) { + fun handleEvent(event: ChooseAccountProviderEvent) { when (event) { - ChooseAccountProviderEvents.Continue -> { + ChooseAccountProviderEvent.Continue -> { selectedAccountProvider?.let { provider -> loginModeState.eventSink( LoginModeEvent.Submit( @@ -48,13 +48,13 @@ class ChooseAccountProviderPresenter( ) } } - is ChooseAccountProviderEvents.SelectAccountProvider -> { + is ChooseAccountProviderEvent.SelectAccountProvider -> { // Ensure that the user does not change the server during processing. if (loginModeState.loginMode is AsyncData.Uninitialized) { selectedAccountProvider = event.accountProvider } } - ChooseAccountProviderEvents.ClearError -> loginModeState.eventSink(LoginModeEvent.ClearError) + ChooseAccountProviderEvent.ClearError -> loginModeState.eventSink(LoginModeEvent.ClearError) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderState.kt index a31e5c464f..398e32d687 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderState.kt @@ -17,7 +17,7 @@ data class ChooseAccountProviderState( val accountProviders: ImmutableList, val selectedAccountProvider: AccountProvider?, val loginModeState: LoginModeState, - val eventSink: (ChooseAccountProviderEvents) -> Unit, + val eventSink: (ChooseAccountProviderEvent) -> Unit, ) { val submitEnabled: Boolean get() = selectedAccountProvider != null && diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStatePreviewParam.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStatePreviewParam.kt index 207f434b42..ceb1cfd6ea 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStatePreviewParam.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStatePreviewParam.kt @@ -71,7 +71,7 @@ fun aChooseAccountProviderState( ), selectedAccountProvider: AccountProvider? = null, loginModeState: LoginModeState = aLoginModeState(), - eventSink: (ChooseAccountProviderEvents) -> Unit = {}, + eventSink: (ChooseAccountProviderEvent) -> Unit = {}, ) = ChooseAccountProviderState( accountProviders = accountProviders.toImmutableList(), selectedAccountProvider = selectedAccountProvider, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt index fdc69ca295..552165a7b2 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt @@ -108,7 +108,7 @@ fun ChooseAccountProviderView( item = alteredItem, selected = item == state.selectedAccountProvider, onClick = { - state.eventSink(ChooseAccountProviderEvents.SelectAccountProvider(item)) + state.eventSink(ChooseAccountProviderEvent.SelectAccountProvider(item)) } ) } @@ -119,7 +119,7 @@ fun ChooseAccountProviderView( text = stringResource(id = CommonStrings.action_continue), showProgress = isLoading, onClick = { - state.eventSink(ChooseAccountProviderEvents.Continue) + state.eventSink(ChooseAccountProviderEvent.Continue) }, enabled = state.submitEnabled || isLoading, modifier = Modifier @@ -131,7 +131,7 @@ fun ChooseAccountProviderView( LoginModeView( loginMode = state.loginModeState.loginMode, onClearError = { - state.eventSink(ChooseAccountProviderEvents.ClearError) + state.eventSink(ChooseAccountProviderEvent.ClearError) }, onLearnMoreClick = onLearnMoreClick, onOAuthDetails = onOAuthDetails, diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenterTest.kt index 4f00bb002b..78f4293489 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenterTest.kt @@ -88,7 +88,7 @@ class ChooseAccountProviderPresenterTest { presenter.test { awaitItem().also { assertThat(it.selectedAccountProvider).isNull() - it.eventSink(ChooseAccountProviderEvents.Continue) + it.eventSink(ChooseAccountProviderEvent.Continue) expectNoEvents() } } @@ -112,11 +112,11 @@ class ChooseAccountProviderPresenterTest { presenter.test { awaitItem().also { assertThat(it.selectedAccountProvider).isNull() - it.eventSink(ChooseAccountProviderEvents.SelectAccountProvider(accountProvider1)) + it.eventSink(ChooseAccountProviderEvent.SelectAccountProvider(accountProvider1)) } awaitItem().also { assertThat(it.selectedAccountProvider).isEqualTo(accountProvider1) - it.eventSink(ChooseAccountProviderEvents.Continue) + it.eventSink(ChooseAccountProviderEvent.Continue) skipItems(1) // Loading // Check an error was returned @@ -124,7 +124,7 @@ class ChooseAccountProviderPresenterTest { assertThat(submittedState.loginModeState.loginMode).isInstanceOf(AsyncData.Failure::class.java) // Assert the error is then cleared - submittedState.eventSink(ChooseAccountProviderEvents.ClearError) + submittedState.eventSink(ChooseAccountProviderEvent.ClearError) val clearedState = awaitItem() assertThat(clearedState.loginModeState.loginMode).isEqualTo(AsyncData.Uninitialized) } @@ -145,15 +145,15 @@ class ChooseAccountProviderPresenterTest { presenter.test { awaitItem().also { assertThat(it.selectedAccountProvider).isNull() - it.eventSink(ChooseAccountProviderEvents.SelectAccountProvider(accountProvider1)) + it.eventSink(ChooseAccountProviderEvent.SelectAccountProvider(accountProvider1)) } awaitItem().also { assertThat(it.selectedAccountProvider).isEqualTo(accountProvider1) - it.eventSink(ChooseAccountProviderEvents.Continue) + it.eventSink(ChooseAccountProviderEvent.Continue) } awaitItem().also { assertThat(it.loginModeState.loginMode.isLoading()).isTrue() - it.eventSink(ChooseAccountProviderEvents.SelectAccountProvider(accountProvider2)) + it.eventSink(ChooseAccountProviderEvent.SelectAccountProvider(accountProvider2)) } expectNoEvents() } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt index cd64638563..4ef7a71966 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt @@ -35,7 +35,7 @@ import org.robolectric.annotation.Config class ChooseAccountProviderViewTest : RobolectricTest() { @Test fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { - val eventSink = EventsRecorder(expectEvents = false) + val eventSink = EventsRecorder(expectEvents = false) ensureCalledOnce { setChooseAccountProviderView( state = aChooseAccountProviderState( @@ -50,7 +50,7 @@ class ChooseAccountProviderViewTest : RobolectricTest() { @Config(qualifiers = "h1024dp") @Test fun `selecting an account provider emits the the expected event`() = runAndroidComposeUiTest { - val eventSink = EventsRecorder() + val eventSink = EventsRecorder() setChooseAccountProviderView( state = aChooseAccountProviderState( accountProviders = listOf( @@ -62,12 +62,12 @@ class ChooseAccountProviderViewTest : RobolectricTest() { ), ) onNodeWithText(ChooseAccountProviderPresenterTest.accountProvider1.title).performClick() - eventSink.assertSingle(ChooseAccountProviderEvents.SelectAccountProvider(ChooseAccountProviderPresenterTest.accountProvider1)) + eventSink.assertSingle(ChooseAccountProviderEvent.SelectAccountProvider(ChooseAccountProviderPresenterTest.accountProvider1)) } @Test fun `when error is displayed - closing the dialog emits the expected event`() = runAndroidComposeUiTest { - val eventSink = EventsRecorder() + val eventSink = EventsRecorder() setChooseAccountProviderView( state = aChooseAccountProviderState( loginModeState = aLoginModeState(loginMode = AsyncData.Failure(AN_EXCEPTION)), @@ -75,7 +75,7 @@ class ChooseAccountProviderViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_ok) - eventSink.assertSingle(ChooseAccountProviderEvents.ClearError) + eventSink.assertSingle(ChooseAccountProviderEvent.ClearError) } private fun AndroidComposeUiTest.setChooseAccountProviderView( From 873843b66919b77dd84b8f79d5cd1452c8f32f11 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:01:42 +0200 Subject: [PATCH 030/112] `ConfirmAccountProviderEvents` -> `ConfirmAccountProviderEvent` --- ...ents.kt => ConfirmAccountProviderEvent.kt} | 8 +-- .../ConfirmAccountProviderPresenter.kt | 8 +-- .../ConfirmAccountProviderState.kt | 2 +- ...ConfirmAccountProviderStatePreviewParam.kt | 2 +- .../ConfirmAccountProviderView.kt | 10 ++-- .../ConfirmAccountProviderPresenterTest.kt | 56 +++++++++---------- 6 files changed, 43 insertions(+), 43 deletions(-) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/{ConfirmAccountProviderEvents.kt => ConfirmAccountProviderEvent.kt} (81%) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderEvents.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderEvent.kt similarity index 81% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderEvents.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderEvent.kt index 9093a8121f..24b6fdfcda 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderEvents.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderEvent.kt @@ -8,11 +8,11 @@ package io.element.android.features.login.impl.screens.confirmaccountprovider -sealed interface ConfirmAccountProviderEvents { - data class UserInputChanged(val accountProvider: String) : ConfirmAccountProviderEvents +sealed interface ConfirmAccountProviderEvent { + data class UserInputChanged(val accountProvider: String) : ConfirmAccountProviderEvent // Carries the account provider to submit (the current field text, or the accepted autocomplete suggestion), // captured from the view at click time so it can't lag behind fast input (e.g. keyboard autofill). - data class Continue(val accountProvider: String) : ConfirmAccountProviderEvents - data object ClearError : ConfirmAccountProviderEvents + data class Continue(val accountProvider: String) : ConfirmAccountProviderEvent + data object ClearError : ConfirmAccountProviderEvent } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt index 9fae52ab0b..627620ff31 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt @@ -110,14 +110,14 @@ class ConfirmAccountProviderPresenter( } } - fun handleEvent(event: ConfirmAccountProviderEvents) { + fun handleEvent(event: ConfirmAccountProviderEvent) { when (event) { - is ConfirmAccountProviderEvents.UserInputChanged -> { + is ConfirmAccountProviderEvent.UserInputChanged -> { userInput = event.accountProvider } // Validate (and persist) the chosen account provider before proceeding. This also enforces the // account-provider access control, which the login submit does not run on its own. - is ConfirmAccountProviderEvents.Continue -> { + is ConfirmAccountProviderEvent.Continue -> { // Apply the accepted account provider (any accepted completion) back into the field so it // renders the full server rather than the typed prefix, and survives the OAuth round-trip. val accountProvider = event.accountProvider.trim() @@ -137,7 +137,7 @@ class ConfirmAccountProviderPresenter( ChangeServerEvent.ChangeServer(AccountProvider(url = accountProviderUrl)) ) } - ConfirmAccountProviderEvents.ClearError -> { + ConfirmAccountProviderEvent.ClearError -> { loginModeState.eventSink(LoginModeEvent.ClearError) changeServerState.eventSink(ChangeServerEvent.ClearError) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt index bf6b0cb0e7..eec082d886 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt @@ -19,7 +19,7 @@ data class ConfirmAccountProviderState( val isAccountCreation: Boolean, val loginModeState: LoginModeState, val changeServerState: ChangeServerState, - val eventSink: (ConfirmAccountProviderEvents) -> Unit + val eventSink: (ConfirmAccountProviderEvent) -> Unit ) { val submitEnabled: Boolean get() = accountProviderInput.isNotBlank() && diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStatePreviewParam.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStatePreviewParam.kt index ab6350bb4d..be85a306ba 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStatePreviewParam.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderStatePreviewParam.kt @@ -40,7 +40,7 @@ private fun aConfirmAccountProviderState( isAccountCreation: Boolean = false, loginModeState: LoginModeState = aLoginModeState(), changeServerState: ChangeServerState = aChangeServerState(), - eventSink: (ConfirmAccountProviderEvents) -> Unit = {}, + eventSink: (ConfirmAccountProviderEvent) -> Unit = {}, ) = ConfirmAccountProviderState( accountProviderInput = accountProviderInput, accountProviderSuggestion = accountProviderSuggestion, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt index 1dab5cd31c..446b969f12 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt @@ -99,7 +99,7 @@ fun ConfirmAccountProviderView( // Dismiss the keyboard and release focus while the account provider is being validated. focusManager.clearFocus(force = true) // Submit the exact field text (plus any accepted suggestion). - eventSink(ConfirmAccountProviderEvents.Continue(accountProviderToSubmit)) + eventSink(ConfirmAccountProviderEvent.Continue(accountProviderToSubmit)) } // Once a validation / login error has been dismissed, return focus and the keyboard to the field so @@ -151,7 +151,7 @@ fun ConfirmAccountProviderView( value = input, onValueChange = { input = it - eventSink(ConfirmAccountProviderEvents.UserInputChanged(it)) + eventSink(ConfirmAccountProviderEvent.UserInputChanged(it)) }, modifier = Modifier .fillMaxWidth() @@ -181,7 +181,7 @@ fun ConfirmAccountProviderView( // showing, Done submits directly. val accepted = input + suggestionSuffix input = accepted - eventSink(ConfirmAccountProviderEvents.UserInputChanged(accepted)) + eventSink(ConfirmAccountProviderEvent.UserInputChanged(accepted)) focusManager.clearFocus(force = true) } else { submit() @@ -197,7 +197,7 @@ fun ConfirmAccountProviderView( role = Role.Button, ) { input = "" - eventSink(ConfirmAccountProviderEvents.UserInputChanged("")) + eventSink(ConfirmAccountProviderEvent.UserInputChanged("")) } ) { Icon( @@ -222,7 +222,7 @@ fun ConfirmAccountProviderView( LoginModeView( loginMode = state.loginModeState.loginMode, - onClearError = { eventSink(ConfirmAccountProviderEvents.ClearError) }, + onClearError = { eventSink(ConfirmAccountProviderEvent.ClearError) }, onLearnMoreClick = onLearnMoreClick, onOAuthDetails = onOAuthDetails, onNeedLoginPassword = onNeedLoginPassword, diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt index 641eea1563..19afbe0f28 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt @@ -68,7 +68,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isEqualTo(LoginMode.PasswordLogin) cancelAndIgnoreRemainingEvents() @@ -87,7 +87,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) cancelAndIgnoreRemainingEvents() @@ -108,7 +108,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) authenticationService.givenOAuthCancelError(AN_EXCEPTION) @@ -133,7 +133,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) defaultOAuthActionFlow.post(OAuthAction.GoBack()) @@ -157,7 +157,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) awaitLoginMode { it is AsyncData.Loading } defaultOAuthActionFlow.post(OAuthAction.GoBack(toUnblock = true)) val cancelFinalState = awaitLoginMode { it is AsyncData.Uninitialized } @@ -180,7 +180,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) authenticationService.givenLoginError(AN_EXCEPTION) @@ -205,7 +205,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) defaultOidcActionFlow.post(OAuthAction.Success("aUrl")) @@ -227,7 +227,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) // The account provider validation fails, so the login is never attempted. val failureState = awaitState { it.changeServerState.changeServerAction is AsyncData.Failure } assertThat(failureState.loginModeState.loginMode).isEqualTo(AsyncData.Uninitialized) @@ -249,13 +249,13 @@ class ConfirmAccountProviderPresenterTest { val initialState = awaitItem() // Submit will return an error while validating the account provider - initialState.eventSink(ConfirmAccountProviderEvents.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) // Check an error was returned val submittedState = awaitState { it.changeServerState.changeServerAction is AsyncData.Failure } // Assert the error is then cleared - submittedState.eventSink(ConfirmAccountProviderEvents.ClearError) + submittedState.eventSink(ConfirmAccountProviderEvent.ClearError) val clearedState = awaitState { it.changeServerState.changeServerAction is AsyncData.Uninitialized } assertThat(clearedState.loginModeState.loginMode).isEqualTo(AsyncData.Uninitialized) } @@ -274,12 +274,12 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvents.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) // Check an error was returned val submittedState = awaitLoginMode { it is AsyncData.Failure } assertThat(submittedState.loginModeState.loginMode.errorOrNull()).isInstanceOf(AccountCreationNotSupported::class.java) // Assert the error is then cleared - submittedState.eventSink(ConfirmAccountProviderEvents.ClearError) + submittedState.eventSink(ConfirmAccountProviderEvent.ClearError) val clearedState = awaitLoginMode { it is AsyncData.Uninitialized } assertThat(clearedState.loginModeState.loginMode).isEqualTo(AsyncData.Uninitialized) cancelAndIgnoreRemainingEvents() @@ -299,7 +299,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvents.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) val submittedState = awaitLoginMode { it is AsyncData.Success } assertThat(submittedState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) cancelAndIgnoreRemainingEvents() @@ -316,7 +316,7 @@ class ConfirmAccountProviderPresenterTest { presenter.test { val initialState = awaitItem() assertThat(initialState.accountProviderSuggestion).isNull() - initialState.eventSink(ConfirmAccountProviderEvents.UserInputChanged("random")) + initialState.eventSink(ConfirmAccountProviderEvent.UserInputChanged("random")) val suggestionState = awaitState { it.accountProviderInput == "random" } // Completion is offered without the https:// scheme, even though history stores the full URL. assertThat(suggestionState.accountProviderSuggestion).isEqualTo("randomcommunity.org") @@ -340,9 +340,9 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvents.UserInputChanged("random")) + initialState.eventSink(ConfirmAccountProviderEvent.UserInputChanged("random")) val suggestionState = awaitState { it.accountProviderSuggestion != null } - suggestionState.eventSink(ConfirmAccountProviderEvents.Continue("randomcommunity.org")) + suggestionState.eventSink(ConfirmAccountProviderEvent.Continue("randomcommunity.org")) awaitLoginMode { it is AsyncData.Success } assertThat(submittedUrls.first()).isEqualTo("https://randomcommunity.org") cancelAndIgnoreRemainingEvents() @@ -360,9 +360,9 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvents.UserInputChanged("random")) + initialState.eventSink(ConfirmAccountProviderEvent.UserInputChanged("random")) val suggestionState = awaitState { it.accountProviderSuggestion != null } - suggestionState.eventSink(ConfirmAccountProviderEvents.Continue("randomcommunity.org")) + suggestionState.eventSink(ConfirmAccountProviderEvent.Continue("randomcommunity.org")) // The field now renders the full accepted server rather than the typed prefix. val appliedState = awaitState { it.accountProviderInput == "randomcommunity.org" } assertThat(appliedState.accountProviderInput).isEqualTo("randomcommunity.org") @@ -381,7 +381,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvents.UserInputChanged("ele")) + initialState.eventSink(ConfirmAccountProviderEvent.UserInputChanged("ele")) val suggestionState = awaitState { it.accountProviderInput == "ele" } assertThat(suggestionState.accountProviderSuggestion).isEqualTo("element.io") } @@ -394,7 +394,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvents.UserInputChanged("matr")) + initialState.eventSink(ConfirmAccountProviderEvent.UserInputChanged("matr")) val suggestionState = awaitState { it.accountProviderInput == "matr" } assertThat(suggestionState.accountProviderSuggestion).isEqualTo("matrix.org") } @@ -409,9 +409,9 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvents.UserInputChanged("https://mat")) + initialState.eventSink(ConfirmAccountProviderEvent.UserInputChanged("https://mat")) assertThat(awaitState { it.accountProviderInput == "https://mat" }.accountProviderSuggestion).isNull() - initialState.eventSink(ConfirmAccountProviderEvents.UserInputChanged("http://mat")) + initialState.eventSink(ConfirmAccountProviderEvent.UserInputChanged("http://mat")) assertThat(awaitState { it.accountProviderInput == "http://mat" }.accountProviderSuggestion).isNull() } } @@ -430,7 +430,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvents.Continue("matrix.org")) + initialState.eventSink(ConfirmAccountProviderEvent.Continue("matrix.org")) awaitLoginMode { it is AsyncData.Success } assertThat(submittedUrls.first()).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL) cancelAndIgnoreRemainingEvents() @@ -453,7 +453,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvents.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) awaitLoginMode { it is AsyncData.Success } assertThat(submittedUrls).containsExactly(AuthenticationConfig.MATRIX_ORG_URL) cancelAndIgnoreRemainingEvents() @@ -472,7 +472,7 @@ class ConfirmAccountProviderPresenterTest { val presenter = createConfirmAccountProviderPresenter(matrixAuthenticationService = authenticationService) presenter.test { val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvents.Continue("@alice:example.org")) + initialState.eventSink(ConfirmAccountProviderEvent.Continue("@alice:example.org")) awaitLoginMode { it is AsyncData.Success } assertThat(submittedUrls.first()).isEqualTo("https://example.org") assertThat(authenticationService.getOAuthUrlLoginHint).isEqualTo("mxid:@alice:example.org") @@ -492,7 +492,7 @@ class ConfirmAccountProviderPresenterTest { val presenter = createConfirmAccountProviderPresenter(matrixAuthenticationService = authenticationService) presenter.test { val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvents.Continue("@alice:example.org:8448")) + initialState.eventSink(ConfirmAccountProviderEvent.Continue("@alice:example.org:8448")) awaitLoginMode { it is AsyncData.Success } assertThat(submittedUrls.first()).isEqualTo("https://example.org:8448") cancelAndIgnoreRemainingEvents() @@ -507,7 +507,7 @@ class ConfirmAccountProviderPresenterTest { val presenter = createConfirmAccountProviderPresenter(matrixAuthenticationService = authenticationService) presenter.test { val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvents.Continue("matrix.org")) + initialState.eventSink(ConfirmAccountProviderEvent.Continue("matrix.org")) awaitLoginMode { it is AsyncData.Success } assertThat(authenticationService.getOAuthUrlLoginHint).isNull() cancelAndIgnoreRemainingEvents() @@ -525,7 +525,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvents.Continue("@alice:example.org")) + initialState.eventSink(ConfirmAccountProviderEvent.Continue("@alice:example.org")) awaitLoginMode { it is AsyncData.Success } assertThat(authenticationService.getOAuthUrlLoginHint).isNull() cancelAndIgnoreRemainingEvents() From 835690a1c28b03c261a66795f80f05a295182a93 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:02:25 +0200 Subject: [PATCH 031/112] `LoginPasswordEvents` -> `LoginPasswordEvent` --- ...asswordEvents.kt => LoginPasswordEvent.kt} | 10 +++--- .../loginpassword/LoginPasswordPresenter.kt | 10 +++--- .../loginpassword/LoginPasswordState.kt | 2 +- .../LoginPasswordStatePreviewParam.kt | 2 +- .../loginpassword/LoginPasswordView.kt | 10 +++--- .../LoginPasswordPresenterTest.kt | 32 ++++++++--------- .../loginpassword/LoginPasswordViewTest.kt | 36 +++++++++---------- 7 files changed, 51 insertions(+), 51 deletions(-) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/{LoginPasswordEvents.kt => LoginPasswordEvent.kt} (64%) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordEvents.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordEvent.kt similarity index 64% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordEvents.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordEvent.kt index 25a003b531..fa9acb5632 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordEvents.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordEvent.kt @@ -8,9 +8,9 @@ package io.element.android.features.login.impl.screens.loginpassword -sealed interface LoginPasswordEvents { - data class SetLogin(val login: String) : LoginPasswordEvents - data class SetPassword(val password: String) : LoginPasswordEvents - data object Submit : LoginPasswordEvents - data object ClearError : LoginPasswordEvents +sealed interface LoginPasswordEvent { + data class SetLogin(val login: String) : LoginPasswordEvent + data class SetPassword(val password: String) : LoginPasswordEvent + data object Submit : LoginPasswordEvent + data object ClearError : LoginPasswordEvent } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenter.kt index 6e28bd8781..864bbc7808 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenter.kt @@ -58,18 +58,18 @@ class LoginPasswordPresenter( } val accountProvider by accountProviderDataSource.flow.collectAsState() - fun handleEvent(event: LoginPasswordEvents) { + fun handleEvent(event: LoginPasswordEvent) { when (event) { - is LoginPasswordEvents.SetLogin -> updateFormState(formState) { + is LoginPasswordEvent.SetLogin -> updateFormState(formState) { copy(login = event.login) } - is LoginPasswordEvents.SetPassword -> updateFormState(formState) { + is LoginPasswordEvent.SetPassword -> updateFormState(formState) { copy(password = event.password) } - LoginPasswordEvents.Submit -> { + LoginPasswordEvent.Submit -> { localCoroutineScope.submit(formState.value, loginAction) } - LoginPasswordEvents.ClearError -> loginAction.value = AsyncData.Uninitialized + LoginPasswordEvent.ClearError -> loginAction.value = AsyncData.Uninitialized } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordState.kt index d8adc7351f..db04e7b7ba 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordState.kt @@ -18,7 +18,7 @@ data class LoginPasswordState( val accountProvider: AccountProvider, val formState: LoginFormState, val loginAction: AsyncData, - val eventSink: (LoginPasswordEvents) -> Unit + val eventSink: (LoginPasswordEvent) -> Unit ) { val submitEnabled: Boolean get() = loginAction !is AsyncData.Failure && diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordStatePreviewParam.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordStatePreviewParam.kt index 4d7201dbc8..ba48e57b5d 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordStatePreviewParam.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordStatePreviewParam.kt @@ -29,7 +29,7 @@ fun aLoginPasswordState( accountProvider: AccountProvider = anAccountProvider(), formState: LoginFormState = LoginFormState.Default, loginAction: AsyncData = AsyncData.Uninitialized, - eventSink: (LoginPasswordEvents) -> Unit = {}, + eventSink: (LoginPasswordEvent) -> Unit = {}, ) = LoginPasswordState( accountProvider = accountProvider, formState = formState, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt index a168b14cc8..9a2dc3097b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt @@ -97,7 +97,7 @@ fun LoginPasswordView( autofillManager?.commit() - state.eventSink(LoginPasswordEvents.Submit) + state.eventSink(LoginPasswordEvent.Submit) } Scaffold( @@ -166,7 +166,7 @@ fun LoginPasswordView( if (state.loginAction is AsyncData.Failure) { LoginErrorDialog(error = state.loginAction.error, onDismiss = { - state.eventSink(LoginPasswordEvents.ClearError) + state.eventSink(LoginPasswordEvent.ClearError) }) } } @@ -202,7 +202,7 @@ private fun LoginForm( onValueChange = { val sanitized = it.sanitize() loginFieldState = sanitized - eventSink(LoginPasswordEvents.SetLogin(sanitized)) + eventSink(LoginPasswordEvent.SetLogin(sanitized)) }, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Email, @@ -220,7 +220,7 @@ private fun LoginForm( role = Role.Button, ) { loginFieldState = "" - eventSink(LoginPasswordEvents.SetLogin("")) + eventSink(LoginPasswordEvent.SetLogin("")) } ) { Icon( @@ -254,7 +254,7 @@ private fun LoginForm( onValueChange = { val sanitized = it.sanitize() passwordFieldState = sanitized - eventSink(LoginPasswordEvents.SetPassword(sanitized)) + eventSink(LoginPasswordEvent.SetPassword(sanitized)) }, placeholder = stringResource(CommonStrings.common_password), visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt index 4dd2859dee..b12c9ec667 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt @@ -54,7 +54,7 @@ class LoginPasswordPresenterTest { val initialState = awaitItem() assertThat(initialState.formState.login).isEqualTo(A_USER_NAME) // Login can be changed - initialState.eventSink.invoke(LoginPasswordEvents.SetLogin(A_USER_NAME_2)) + initialState.eventSink.invoke(LoginPasswordEvent.SetLogin(A_USER_NAME_2)) val loginChangedState = awaitItem() assertThat(loginChangedState.formState.login).isEqualTo(A_USER_NAME_2) } @@ -71,11 +71,11 @@ class LoginPasswordPresenterTest { authenticationService = authenticationService, ).test { val initialState = awaitItem() - initialState.eventSink.invoke(LoginPasswordEvents.SetLogin(A_USER_NAME)) + initialState.eventSink.invoke(LoginPasswordEvent.SetLogin(A_USER_NAME)) val loginState = awaitItem() assertThat(loginState.formState).isEqualTo(LoginFormState(login = A_USER_NAME, password = "")) assertThat(loginState.submitEnabled).isFalse() - initialState.eventSink.invoke(LoginPasswordEvents.SetPassword(A_PASSWORD)) + initialState.eventSink.invoke(LoginPasswordEvent.SetPassword(A_PASSWORD)) val loginAndPasswordState = awaitItem() assertThat(loginAndPasswordState.formState).isEqualTo(LoginFormState(login = A_USER_NAME, password = A_PASSWORD)) assertThat(loginAndPasswordState.submitEnabled).isTrue() @@ -93,11 +93,11 @@ class LoginPasswordPresenterTest { authenticationService = authenticationService, ).test { val initialState = awaitItem() - initialState.eventSink.invoke(LoginPasswordEvents.SetLogin(A_USER_NAME)) - initialState.eventSink.invoke(LoginPasswordEvents.SetPassword(A_PASSWORD)) + initialState.eventSink.invoke(LoginPasswordEvent.SetLogin(A_USER_NAME)) + initialState.eventSink.invoke(LoginPasswordEvent.SetPassword(A_PASSWORD)) skipItems(1) val loginAndPasswordState = awaitItem() - loginAndPasswordState.eventSink.invoke(LoginPasswordEvents.Submit) + loginAndPasswordState.eventSink.invoke(LoginPasswordEvent.Submit) val submitState = awaitItem() assertThat(submitState.loginAction).isInstanceOf(AsyncData.Loading::class.java) val loggedInState = awaitItem() @@ -118,11 +118,11 @@ class LoginPasswordPresenterTest { appPreferencesStore = appPreferencesStore, ).test { val initialState = awaitItem() - initialState.eventSink.invoke(LoginPasswordEvents.SetLogin(A_USER_NAME)) - initialState.eventSink.invoke(LoginPasswordEvents.SetPassword(A_PASSWORD)) + initialState.eventSink.invoke(LoginPasswordEvent.SetLogin(A_USER_NAME)) + initialState.eventSink.invoke(LoginPasswordEvent.SetPassword(A_PASSWORD)) skipItems(1) val loginAndPasswordState = awaitItem() - loginAndPasswordState.eventSink.invoke(LoginPasswordEvents.Submit) + loginAndPasswordState.eventSink.invoke(LoginPasswordEvent.Submit) skipItems(1) val loggedInState = awaitItem() assertThat(loggedInState.loginAction).isEqualTo(AsyncData.Success(A_SESSION_ID)) @@ -142,12 +142,12 @@ class LoginPasswordPresenterTest { authenticationService = authenticationService, ).test { val initialState = awaitItem() - initialState.eventSink.invoke(LoginPasswordEvents.SetLogin(A_USER_NAME)) - initialState.eventSink.invoke(LoginPasswordEvents.SetPassword(A_PASSWORD)) + initialState.eventSink.invoke(LoginPasswordEvent.SetLogin(A_USER_NAME)) + initialState.eventSink.invoke(LoginPasswordEvent.SetPassword(A_PASSWORD)) skipItems(1) val loginAndPasswordState = awaitItem() authenticationService.givenLoginError(AN_EXCEPTION) - loginAndPasswordState.eventSink.invoke(LoginPasswordEvents.Submit) + loginAndPasswordState.eventSink.invoke(LoginPasswordEvent.Submit) val submitState = awaitItem() assertThat(submitState.loginAction).isInstanceOf(AsyncData.Loading::class.java) val loggedInState = awaitItem() @@ -166,19 +166,19 @@ class LoginPasswordPresenterTest { authenticationService = authenticationService, ).test { val initialState = awaitItem() - initialState.eventSink.invoke(LoginPasswordEvents.SetLogin(A_USER_NAME)) - initialState.eventSink.invoke(LoginPasswordEvents.SetPassword(A_PASSWORD)) + initialState.eventSink.invoke(LoginPasswordEvent.SetLogin(A_USER_NAME)) + initialState.eventSink.invoke(LoginPasswordEvent.SetPassword(A_PASSWORD)) skipItems(1) val loginAndPasswordState = awaitItem() authenticationService.givenLoginError(AN_EXCEPTION) - loginAndPasswordState.eventSink.invoke(LoginPasswordEvents.Submit) + loginAndPasswordState.eventSink.invoke(LoginPasswordEvent.Submit) val submitState = awaitItem() assertThat(submitState.loginAction).isInstanceOf(AsyncData.Loading::class.java) val loggedInState = awaitItem() // Check an error was returned assertThat(loggedInState.loginAction).isEqualTo(AsyncData.Failure(AN_EXCEPTION)) // Assert the error is then cleared - loggedInState.eventSink(LoginPasswordEvents.ClearError) + loggedInState.eventSink(LoginPasswordEvent.ClearError) val clearedState = awaitItem() assertThat(clearedState.loginAction).isEqualTo(AsyncData.Uninitialized) } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordViewTest.kt index d76c94d5ea..32ce44366f 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordViewTest.kt @@ -39,7 +39,7 @@ import org.robolectric.annotation.Config class LoginPasswordViewTest : RobolectricTest() { @Test fun `clicking on back invoke back callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> setLoginPasswordView( aLoginPasswordState( @@ -53,7 +53,7 @@ class LoginPasswordViewTest : RobolectricTest() { @Test fun `changing login invokes the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setLoginPasswordView( aLoginPasswordState( eventSink = eventsRecorder, @@ -62,13 +62,13 @@ class LoginPasswordViewTest : RobolectricTest() { val userNameHint = activity!!.getString(CommonStrings.common_username) onNodeWithText(userNameHint).performTextInput(A_USER_NAME) eventsRecorder.assertSingle( - LoginPasswordEvents.SetLogin(A_USER_NAME) + LoginPasswordEvent.SetLogin(A_USER_NAME) ) } @Test fun `changing login removes new lines the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setLoginPasswordView( aLoginPasswordState( eventSink = eventsRecorder, @@ -77,13 +77,13 @@ class LoginPasswordViewTest : RobolectricTest() { val userNameHint = activity!!.getString(CommonStrings.common_username) onNodeWithText(userNameHint).performTextInput("a\nb") eventsRecorder.assertSingle( - LoginPasswordEvents.SetLogin("ab") + LoginPasswordEvent.SetLogin("ab") ) } @Test fun `changing login removes carriage returns the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setLoginPasswordView( aLoginPasswordState( eventSink = eventsRecorder, @@ -92,13 +92,13 @@ class LoginPasswordViewTest : RobolectricTest() { val userNameHint = activity!!.getString(CommonStrings.common_username) onNodeWithText(userNameHint).performTextInput("a\r\nb") eventsRecorder.assertSingle( - LoginPasswordEvents.SetLogin("ab") + LoginPasswordEvent.SetLogin("ab") ) } @Test fun `clearing login invokes the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setLoginPasswordView( aLoginPasswordState( formState = aLoginFormState(A_USER_NAME), @@ -108,13 +108,13 @@ class LoginPasswordViewTest : RobolectricTest() { val a11yClear = activity!!.getString(CommonStrings.action_clear) onNodeWithContentDescription(a11yClear).performClick() eventsRecorder.assertSingle( - LoginPasswordEvents.SetLogin("") + LoginPasswordEvent.SetLogin("") ) } @Test fun `changing password invokes the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setLoginPasswordView( aLoginPasswordState( eventSink = eventsRecorder, @@ -123,13 +123,13 @@ class LoginPasswordViewTest : RobolectricTest() { val userNameHint = activity!!.getString(CommonStrings.common_password) onNodeWithText(userNameHint).performTextInput(A_PASSWORD) eventsRecorder.assertSingle( - LoginPasswordEvents.SetPassword(A_PASSWORD) + LoginPasswordEvent.SetPassword(A_PASSWORD) ) } @Test fun `changing password removes carriage returns the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setLoginPasswordView( aLoginPasswordState( eventSink = eventsRecorder, @@ -138,13 +138,13 @@ class LoginPasswordViewTest : RobolectricTest() { val passwordHint = activity!!.getString(CommonStrings.common_password) onNodeWithText(passwordHint).performTextInput("a\r\nb") eventsRecorder.assertSingle( - LoginPasswordEvents.SetPassword("ab") + LoginPasswordEvent.SetPassword("ab") ) } @Test fun `reveal password makes the password visible`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) setLoginPasswordView( aLoginPasswordState( formState = aLoginFormState(password = A_PASSWORD), @@ -165,7 +165,7 @@ class LoginPasswordViewTest : RobolectricTest() { @Test fun `when login is empty, continue button is not enabled`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) setLoginPasswordView( aLoginPasswordState( formState = aLoginFormState(password = A_PASSWORD), @@ -178,7 +178,7 @@ class LoginPasswordViewTest : RobolectricTest() { @Test fun `when password is empty, continue button is not enabled`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) setLoginPasswordView( aLoginPasswordState( formState = aLoginFormState(login = A_USER_NAME), @@ -192,7 +192,7 @@ class LoginPasswordViewTest : RobolectricTest() { @Config(qualifiers = "h1024dp") @Test fun `clicking on Continue sends expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setLoginPasswordView( aLoginPasswordState( formState = aLoginFormState(login = A_USER_NAME, password = A_PASSWORD), @@ -203,7 +203,7 @@ class LoginPasswordViewTest : RobolectricTest() { onNodeWithText(continueStr).assertIsEnabled() clickOn(CommonStrings.action_continue) eventsRecorder.assertSingle( - LoginPasswordEvents.Submit + LoginPasswordEvent.Submit ) } } From b75d8bb6c1ca0b41b4fa8e2c3a1ed14d830a7992 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:03:05 +0200 Subject: [PATCH 032/112] `OnBoardingEvents` -> `OnBoardingEvent` --- ...OnBoardingEvents.kt => OnBoardingEvent.kt} | 8 ++--- .../screens/onboarding/OnBoardingPresenter.kt | 8 ++--- .../screens/onboarding/OnBoardingState.kt | 2 +- .../onboarding/OnBoardingStatePreviewParam.kt | 2 +- .../impl/screens/onboarding/OnBoardingView.kt | 6 ++-- .../onboarding/OnBoardingPresenterTest.kt | 8 ++--- .../screens/onboarding/OnboardingViewTest.kt | 32 +++++++++---------- 7 files changed, 33 insertions(+), 33 deletions(-) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/{OnBoardingEvents.kt => OnBoardingEvent.kt} (70%) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingEvents.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingEvent.kt similarity index 70% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingEvents.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingEvent.kt index 6101b21ded..4b3993e2d1 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingEvents.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingEvent.kt @@ -8,11 +8,11 @@ package io.element.android.features.login.impl.screens.onboarding -sealed interface OnBoardingEvents { +sealed interface OnBoardingEvent { data class OnSignIn( val defaultAccountProvider: String - ) : OnBoardingEvents + ) : OnBoardingEvent - data object OnVersionClick : OnBoardingEvents - data object ClearError : OnBoardingEvents + data object OnVersionClick : OnBoardingEvent + data object ClearError : OnBoardingEvent } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt index e9bdb5e1e9..7e48ae22e8 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt @@ -101,9 +101,9 @@ class OnBoardingPresenter( val loginModeState = loginModePresenter.present() - fun handleEvent(event: OnBoardingEvents) { + fun handleEvent(event: OnBoardingEvent) { when (event) { - is OnBoardingEvents.OnSignIn -> localCoroutineScope.launch { + is OnBoardingEvent.OnSignIn -> localCoroutineScope.launch { // Ensure that the current account provider is set accountProviderDataSource.setUrl(event.defaultAccountProvider) loginModeState.eventSink( @@ -115,8 +115,8 @@ class OnBoardingPresenter( ) ) } - OnBoardingEvents.ClearError -> loginModeState.eventSink(LoginModeEvent.ClearError) - OnBoardingEvents.OnVersionClick -> { + OnBoardingEvent.ClearError -> loginModeState.eventSink(LoginModeEvent.ClearError) + OnBoardingEvent.OnVersionClick -> { if (canReportBug) { if (multipleTapToUnlock.unlock(localCoroutineScope)) { showReportBug = true diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingState.kt index 4fb4b8090e..97ebed67f6 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingState.kt @@ -26,7 +26,7 @@ data class OnBoardingState( @DrawableRes val onBoardingLogoResId: Int?, val loginModeState: LoginModeState, - val eventSink: (OnBoardingEvents) -> Unit, + val eventSink: (OnBoardingEvent) -> Unit, ) { val submitEnabled: Boolean get() = defaultAccountProvider != null && diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStatePreviewParam.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStatePreviewParam.kt index d04f85f2a0..c7d89b21d5 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStatePreviewParam.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStatePreviewParam.kt @@ -50,7 +50,7 @@ fun anOnBoardingState( @DrawableRes customLogoResId: Int? = null, loginModeState: LoginModeState = aLoginModeState(), - eventSink: (OnBoardingEvents) -> Unit = {}, + eventSink: (OnBoardingEvent) -> Unit = {}, ) = OnBoardingState( isAddingAccount = isAddingAccount, showBackButton = showBackButton, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt index 78faed7543..bd257d0891 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt @@ -78,7 +78,7 @@ fun OnBoardingView( LoginModeView( loginMode = state.loginModeState.loginMode, onClearError = { - state.eventSink(OnBoardingEvents.ClearError) + state.eventSink(OnBoardingEvent.ClearError) }, onLearnMoreClick = onLearnMoreClick, onOAuthDetails = onOAuthDetails, @@ -306,7 +306,7 @@ private fun OnBoardingButtons( text = stringResource(id = R.string.screen_onboarding_sign_in_to, defaultAccountProvider), showProgress = isLoading, onClick = { - state.eventSink(OnBoardingEvents.OnSignIn(defaultAccountProvider)) + state.eventSink(OnBoardingEvent.OnSignIn(defaultAccountProvider)) }, enabled = state.submitEnabled || isLoading, modifier = Modifier @@ -336,7 +336,7 @@ private fun OnBoardingButtons( Text( modifier = Modifier .clickable(role = Role.Button) { - state.eventSink(OnBoardingEvents.OnVersionClick) + state.eventSink(OnBoardingEvent.OnVersionClick) } .padding(16.dp), text = stringResource(id = R.string.screen_onboarding_app_version, state.version), diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt index 3c2e4b574f..1088a87538 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt @@ -153,7 +153,7 @@ class OnBoardingPresenterTest { awaitItem().also { state -> assertThat(state.canReportBug).isFalse() repeat(7) { - state.eventSink(OnBoardingEvents.OnVersionClick) + state.eventSink(OnBoardingEvent.OnVersionClick) } } expectNoEvents() @@ -168,7 +168,7 @@ class OnBoardingPresenterTest { awaitItem().also { state -> assertThat(state.canReportBug).isFalse() repeat(7) { - state.eventSink(OnBoardingEvents.OnVersionClick) + state.eventSink(OnBoardingEvent.OnVersionClick) } } assertThat(awaitItem().canReportBug).isTrue() @@ -272,7 +272,7 @@ class OnBoardingPresenterTest { awaitItem().also { assertThat(it.defaultAccountProvider).isEqualTo(A_HOMESERVER_URL) assertThat(accountProviderDataSource.flow.first().url).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL) - it.eventSink(OnBoardingEvents.OnSignIn(A_HOMESERVER_URL_2)) + it.eventSink(OnBoardingEvent.OnSignIn(A_HOMESERVER_URL_2)) skipItems(1) // Loading // Account data source has been updated assertThat(accountProviderDataSource.flow.first().url).isEqualTo(A_HOMESERVER_URL_2) @@ -281,7 +281,7 @@ class OnBoardingPresenterTest { assertThat(submittedState.loginModeState.loginMode).isInstanceOf(AsyncData.Failure::class.java) // Assert the error is then cleared - submittedState.eventSink(OnBoardingEvents.ClearError) + submittedState.eventSink(OnBoardingEvent.ClearError) val clearedState = awaitItem() assertThat(clearedState.loginModeState.loginMode).isEqualTo(AsyncData.Uninitialized) } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt index dfa4119711..88c9acf55f 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt @@ -39,7 +39,7 @@ import org.junit.Test class OnboardingViewTest : RobolectricTestParameter() { @Test fun `when can create account - clicking on create account calls the expected callback`() = runAndroidComposeUiTest { - val eventSink = EventsRecorder(expectEvents = false) + val eventSink = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> setOnboardingView( state = anOnBoardingState( @@ -58,7 +58,7 @@ class OnboardingViewTest : RobolectricTestParameter() { @Test fun `when can go back - clicking on back calls the expected callback`() = runAndroidComposeUiTest { - val eventSink = EventsRecorder(expectEvents = false) + val eventSink = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> setOnboardingView( state = anOnBoardingState( @@ -73,7 +73,7 @@ class OnboardingViewTest : RobolectricTestParameter() { @Test fun `when can login with QR code - clicking on sign in with QR code calls the expected callback`() = runAndroidComposeUiTest { - val eventSink = EventsRecorder(expectEvents = false) + val eventSink = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> setOnboardingView( state = anOnBoardingState( @@ -93,7 +93,7 @@ class OnboardingViewTest : RobolectricTestParameter() { "cannot search account provider" to true, ) ) = runAndroidComposeUiTest { - val eventSink = EventsRecorder(expectEvents = false) + val eventSink = EventsRecorder(expectEvents = false) ensureCalledOnceWithParam(mustChooseAccountProvider) { callback -> setOnboardingView( state = anOnBoardingState( @@ -114,7 +114,7 @@ class OnboardingViewTest : RobolectricTestParameter() { "cannot search account provider" to true, ) ) = runAndroidComposeUiTest { - val eventSink = EventsRecorder(expectEvents = false) + val eventSink = EventsRecorder(expectEvents = false) ensureCalledOnceWithParam(mustChooseAccountProvider) { callback -> setOnboardingView( state = anOnBoardingState( @@ -131,7 +131,7 @@ class OnboardingViewTest : RobolectricTestParameter() { @Test fun `when sign in to pre defined account provider - clicking on button emits the expected event`() = runAndroidComposeUiTest { - val eventSink = EventsRecorder() + val eventSink = EventsRecorder() setOnboardingView( state = anOnBoardingState( defaultAccountProvider = "element.io", @@ -140,12 +140,12 @@ class OnboardingViewTest : RobolectricTestParameter() { ) val buttonText = activity!!.getString(R.string.screen_onboarding_sign_in_to, "element.io") onNodeWithText(buttonText).performClick() - eventSink.assertSingle(OnBoardingEvents.OnSignIn("element.io")) + eventSink.assertSingle(OnBoardingEvent.OnSignIn("element.io")) } @Test fun `when error is displayed - closing the dialog emits the expected event`() = runAndroidComposeUiTest { - val eventSink = EventsRecorder() + val eventSink = EventsRecorder() setOnboardingView( state = anOnBoardingState( defaultAccountProvider = "element.io", @@ -154,12 +154,12 @@ class OnboardingViewTest : RobolectricTestParameter() { ), ) clickOn(CommonStrings.action_ok) - eventSink.assertSingle(OnBoardingEvents.ClearError) + eventSink.assertSingle(OnBoardingEvent.ClearError) } @Test fun `clicking on report a problem calls the sign in callback`() = runAndroidComposeUiTest { - val eventSink = EventsRecorder(expectEvents = false) + val eventSink = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> setOnboardingView( state = anOnBoardingState( @@ -176,7 +176,7 @@ class OnboardingViewTest : RobolectricTestParameter() { @Test fun `clicking on settings calls the developer settings callback`() = runAndroidComposeUiTest { - val eventSink = EventsRecorder(expectEvents = false) + val eventSink = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> setOnboardingView( state = anOnBoardingState( @@ -192,7 +192,7 @@ class OnboardingViewTest : RobolectricTestParameter() { @Test fun `cannot report a problem when the feature is disabled`() = runAndroidComposeUiTest { - val eventSink = EventsRecorder(expectEvents = false) + val eventSink = EventsRecorder(expectEvents = false) setOnboardingView( state = anOnBoardingState( canReportBug = false, @@ -205,7 +205,7 @@ class OnboardingViewTest : RobolectricTestParameter() { @Test fun `when success PasswordLogin - the expected callback is invoked and the event is received`() = runAndroidComposeUiTest { - val eventSink = EventsRecorder() + val eventSink = EventsRecorder() ensureCalledOnce { callback -> setOnboardingView( state = anOnBoardingState( @@ -215,12 +215,12 @@ class OnboardingViewTest : RobolectricTestParameter() { onNeedLoginPassword = callback, ) } - eventSink.assertSingle(OnBoardingEvents.ClearError) + eventSink.assertSingle(OnBoardingEvent.ClearError) } @Test fun `when success Oidc - the expected callback is invoked and the event is received`() = runAndroidComposeUiTest { - val eventSink = EventsRecorder() + val eventSink = EventsRecorder() val oAuthDetails = OAuthDetails("aUrl") ensureCalledOnceWithParam(oAuthDetails) { callback -> setOnboardingView( @@ -231,7 +231,7 @@ class OnboardingViewTest : RobolectricTestParameter() { onOAuthDetails = callback, ) } - eventSink.assertSingle(OnBoardingEvents.ClearError) + eventSink.assertSingle(OnBoardingEvent.ClearError) } private fun AndroidComposeUiTest.setOnboardingView( From 0bf671a3050d26a721ba267ea1137fc20a93ff69 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:03:42 +0200 Subject: [PATCH 033/112] `QrCodeIntroEvents` -> `QrCodeIntroEvent` --- .../intro/{QrCodeIntroEvents.kt => QrCodeIntroEvent.kt} | 4 ++-- .../login/impl/screens/qrcode/intro/QrCodeIntroPresenter.kt | 4 ++-- .../login/impl/screens/qrcode/intro/QrCodeIntroState.kt | 2 +- .../impl/screens/qrcode/intro/QrCodeIntroStatePreviewParam.kt | 2 +- .../login/impl/screens/qrcode/intro/QrCodeIntroView.kt | 2 +- .../impl/screens/qrcode/intro/QrCodeIntroPresenterTest.kt | 4 ++-- .../login/impl/screens/qrcode/intro/QrCodeIntroViewTest.kt | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/{QrCodeIntroEvents.kt => QrCodeIntroEvent.kt} (79%) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroEvents.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroEvent.kt similarity index 79% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroEvents.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroEvent.kt index 11abd63683..a4471d8a92 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroEvents.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroEvent.kt @@ -8,6 +8,6 @@ package io.element.android.features.login.impl.screens.qrcode.intro -sealed interface QrCodeIntroEvents { - data object Continue : QrCodeIntroEvents +sealed interface QrCodeIntroEvent { + data object Continue : QrCodeIntroEvent } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroPresenter.kt index 13888fe23f..0781942216 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroPresenter.kt @@ -40,9 +40,9 @@ class QrCodeIntroPresenter( } } - fun handleEvent(event: QrCodeIntroEvents) { + fun handleEvent(event: QrCodeIntroEvent) { when (event) { - QrCodeIntroEvents.Continue -> if (cameraPermissionState.permissionGranted) { + QrCodeIntroEvent.Continue -> if (cameraPermissionState.permissionGranted) { canContinue = true } else { pendingPermissionRequest = true diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroState.kt index d20a3f58d9..ab84743404 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroState.kt @@ -15,5 +15,5 @@ data class QrCodeIntroState( val desktopAppName: String, val cameraPermissionState: PermissionsState, val canContinue: Boolean, - val eventSink: (QrCodeIntroEvents) -> Unit + val eventSink: (QrCodeIntroEvent) -> Unit ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroStatePreviewParam.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroStatePreviewParam.kt index b9c286dc16..5e5fbac8b1 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroStatePreviewParam.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroStatePreviewParam.kt @@ -30,7 +30,7 @@ fun aQrCodeIntroState( permission = Manifest.permission.CAMERA, ), canContinue: Boolean = false, - eventSink: (QrCodeIntroEvents) -> Unit = {}, + eventSink: (QrCodeIntroEvent) -> Unit = {}, ) = QrCodeIntroState( appName = appName, desktopAppName = desktopAppName, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroView.kt index 08a40b63c5..2691c0cbf5 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroView.kt @@ -91,7 +91,7 @@ private fun ColumnScope.Buttons( text = stringResource(id = R.string.screen_qr_code_login_initial_state_button_title), modifier = Modifier.fillMaxWidth(), onClick = { - state.eventSink.invoke(QrCodeIntroEvents.Continue) + state.eventSink.invoke(QrCodeIntroEvent.Continue) } ) } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroPresenterTest.kt index aabcf12e25..02bace008e 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroPresenterTest.kt @@ -37,7 +37,7 @@ class QrCodeIntroPresenterTest { val permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionsPresenter) val presenter = createQrCodeIntroPresenter(permissionsPresenterFactory = permissionsPresenterFactory) presenter.test { - awaitItem().eventSink(QrCodeIntroEvents.Continue) + awaitItem().eventSink(QrCodeIntroEvent.Continue) assertThat(awaitItem().canContinue).isTrue() } } @@ -48,7 +48,7 @@ class QrCodeIntroPresenterTest { val permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionsPresenter) val presenter = createQrCodeIntroPresenter(permissionsPresenterFactory = permissionsPresenterFactory) presenter.test { - awaitItem().eventSink(QrCodeIntroEvents.Continue) + awaitItem().eventSink(QrCodeIntroEvent.Continue) assertThat(awaitItem().cameraPermissionState.showDialog).isTrue() } } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroViewTest.kt index 5720c68ab4..3bb600ae55 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroViewTest.kt @@ -59,12 +59,12 @@ class QrCodeIntroViewTest : RobolectricTest() { @Test fun `on submit button clicked - emits the Continue event`() = runAndroidComposeUiTest { - val eventRecorder = EventsRecorder() + val eventRecorder = EventsRecorder() setQrCodeIntroView( state = aQrCodeIntroState(eventSink = eventRecorder), ) clickOn(R.string.screen_qr_code_login_initial_state_button_title) - eventRecorder.assertSingle(QrCodeIntroEvents.Continue) + eventRecorder.assertSingle(QrCodeIntroEvent.Continue) } private fun AndroidComposeUiTest.setQrCodeIntroView( From a039fcd65572135d7949fd79842e0b7de18fb226 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:04:23 +0200 Subject: [PATCH 034/112] `QrCodeScanEvents` -> `QrCodeScanEvent` --- .../qrcode/scan/{QrCodeScanEvents.kt => QrCodeScanEvent.kt} | 6 +++--- .../login/impl/screens/qrcode/scan/QrCodeScanPresenter.kt | 6 +++--- .../login/impl/screens/qrcode/scan/QrCodeScanState.kt | 2 +- .../impl/screens/qrcode/scan/QrCodeScanStatePreviewParam.kt | 2 +- .../login/impl/screens/qrcode/scan/QrCodeScanView.kt | 4 ++-- .../impl/screens/qrcode/scan/QrCodeScanPresenterTest.kt | 6 +++--- 6 files changed, 13 insertions(+), 13 deletions(-) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/{QrCodeScanEvents.kt => QrCodeScanEvent.kt} (81%) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanEvents.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanEvent.kt similarity index 81% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanEvents.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanEvent.kt index f5804aceb0..d1c91b9b42 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanEvents.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanEvent.kt @@ -8,7 +8,7 @@ package io.element.android.features.login.impl.screens.qrcode.scan -sealed interface QrCodeScanEvents { - data class QrCodeScanned(val code: ByteArray) : QrCodeScanEvents - data object TryAgain : QrCodeScanEvents +sealed interface QrCodeScanEvent { + data class QrCodeScanned(val code: ByteArray) : QrCodeScanEvent + data object TryAgain : QrCodeScanEvent } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanPresenter.kt index 5e298604a4..4075a9d2cd 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanPresenter.kt @@ -52,13 +52,13 @@ class QrCodeScanPresenter( authenticationAction.value = AsyncAction.Failure(it) } - fun handleEvent(event: QrCodeScanEvents) { + fun handleEvent(event: QrCodeScanEvent) { when (event) { - QrCodeScanEvents.TryAgain -> { + QrCodeScanEvent.TryAgain -> { isScanning = true authenticationAction.value = AsyncAction.Uninitialized } - is QrCodeScanEvents.QrCodeScanned -> { + is QrCodeScanEvent.QrCodeScanned -> { isScanning = false coroutineScope.getQrCodeData(authenticationAction, event.code) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanState.kt index 0e64bfc6a6..5d9fca2543 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanState.kt @@ -14,5 +14,5 @@ import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginDat data class QrCodeScanState( val isScanning: Boolean, val authenticationAction: AsyncAction, - val eventSink: (QrCodeScanEvents) -> Unit + val eventSink: (QrCodeScanEvent) -> Unit ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanStatePreviewParam.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanStatePreviewParam.kt index 19385b6df1..24164b94c9 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanStatePreviewParam.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanStatePreviewParam.kt @@ -46,7 +46,7 @@ open class QrCodeScanStatePreviewParam : PreviewParameterProvider = AsyncAction.Uninitialized, - eventSink: (QrCodeScanEvents) -> Unit = {}, + eventSink: (QrCodeScanEvent) -> Unit = {}, ) = QrCodeScanState( isScanning = isScanning, authenticationAction = authenticationAction, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanView.kt index 72d624a921..372a46788b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanView.kt @@ -105,7 +105,7 @@ private fun Content( ) { QrCodeCameraView( modifier = Modifier.fillMaxSize(), - onScanQrCode = { state.eventSink.invoke(QrCodeScanEvents.QrCodeScanned(it)) }, + onScanQrCode = { state.eventSink.invoke(QrCodeScanEvent.QrCodeScanned(it)) }, isScanning = state.isScanning, ) } @@ -125,7 +125,7 @@ private fun ColumnScope.Buttons( .fillMaxWidth() .padding(bottom = 16.dp), onClick = { - state.eventSink.invoke(QrCodeScanEvents.TryAgain) + state.eventSink.invoke(QrCodeScanEvent.TryAgain) } ) Column( diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanPresenterTest.kt index d4f23a452c..a13590a1bc 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanPresenterTest.kt @@ -50,7 +50,7 @@ class QrCodeScanPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(QrCodeScanEvents.QrCodeScanned(byteArrayOf())) + initialState.eventSink(QrCodeScanEvent.QrCodeScanned(byteArrayOf())) assertThat(awaitItem().isScanning).isFalse() assertThat(awaitItem().authenticationAction.isLoading()).isTrue() assertThat(awaitItem().authenticationAction.isSuccess()).isTrue() @@ -65,14 +65,14 @@ class QrCodeScanPresenterTest { val presenter = createQrCodeScanPresenter(qrCodeLoginDataFactory = qrCodeLoginDataFactory) presenter.test { val initialState = awaitItem() - initialState.eventSink(QrCodeScanEvents.QrCodeScanned(byteArrayOf())) + initialState.eventSink(QrCodeScanEvent.QrCodeScanned(byteArrayOf())) assertThat(awaitItem().isScanning).isFalse() assertThat(awaitItem().authenticationAction.isLoading()).isTrue() val errorState = awaitItem() assertThat(errorState.authenticationAction.isFailure()).isTrue() - errorState.eventSink(QrCodeScanEvents.TryAgain) + errorState.eventSink(QrCodeScanEvent.TryAgain) assertThat(awaitItem().isScanning).isTrue() assertThat(awaitItem().authenticationAction.isUninitialized()).isTrue() } From 93efe3348f7860477a7c170fb87674b1d5f55b5f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:05:04 +0200 Subject: [PATCH 035/112] `DirectLogoutEvents` -> `DirectLogoutEvent` --- .../ChooseSelfVerificationModePresenter.kt | 4 ++-- ...oseSessionVerificationModePresenterTest.kt | 6 ++--- ...ctLogoutEvents.kt => DirectLogoutEvent.kt} | 6 ++--- .../logout/api/direct/DirectLogoutState.kt | 2 +- .../direct/DirectLogoutStatePreviewParam.kt | 2 +- .../impl/direct/DefaultDirectLogoutView.kt | 8 +++---- .../impl/direct/DirectLogoutPresenter.kt | 8 +++---- .../direct/DefaultDirectLogoutViewTest.kt | 22 +++++++++---------- .../impl/direct/DirectLogoutPresenterTest.kt | 22 +++++++++---------- .../impl/root/PreferencesRootNode.kt | 4 ++-- 10 files changed, 42 insertions(+), 42 deletions(-) rename features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/{DirectLogoutEvents.kt => DirectLogoutEvent.kt} (78%) diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModePresenter.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModePresenter.kt index 2e37cbe8a5..f19c323d52 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModePresenter.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModePresenter.kt @@ -15,7 +15,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import dev.zacsweers.metro.Inject -import io.element.android.features.logout.api.direct.DirectLogoutEvents +import io.element.android.features.logout.api.direct.DirectLogoutEvent import io.element.android.features.logout.api.direct.DirectLogoutState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter @@ -67,7 +67,7 @@ class ChooseSelfVerificationModePresenter( fun handleEvent(event: ChooseSelfVerificationModeEvent) { when (event) { - ChooseSelfVerificationModeEvent.SignOut -> directLogoutState.eventSink(DirectLogoutEvents.Logout(ignoreSdkError = false)) + ChooseSelfVerificationModeEvent.SignOut -> directLogoutState.eventSink(DirectLogoutEvent.Logout(ignoreSdkError = false)) } } diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSessionVerificationModePresenterTest.kt b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSessionVerificationModePresenterTest.kt index 697c51f776..8ceb683993 100644 --- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSessionVerificationModePresenterTest.kt +++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSessionVerificationModePresenterTest.kt @@ -9,7 +9,7 @@ package io.element.android.features.ftue.impl.sessionverification.choosemode import com.google.common.truth.Truth.assertThat -import io.element.android.features.logout.api.direct.DirectLogoutEvents +import io.element.android.features.logout.api.direct.DirectLogoutEvent import io.element.android.features.logout.api.direct.DirectLogoutState import io.element.android.features.logout.api.direct.aDirectLogoutState import io.element.android.libraries.architecture.AsyncData @@ -112,7 +112,7 @@ class ChooseSessionVerificationModePresenterTest { @Test fun `sing out action triggers a direct logout`() = runTest { - val logoutEventRecorder = lambdaRecorder {} + val logoutEventRecorder = lambdaRecorder {} val logoutPresenter = Presenter { aDirectLogoutState(eventSink = logoutEventRecorder) } @@ -121,7 +121,7 @@ class ChooseSessionVerificationModePresenterTest { val initial = awaitItem() initial.eventSink(ChooseSelfVerificationModeEvent.SignOut) logoutEventRecorder.assertions().isCalledOnce() - .with(value(DirectLogoutEvents.Logout(ignoreSdkError = false))) + .with(value(DirectLogoutEvent.Logout(ignoreSdkError = false))) } } diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutEvents.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutEvent.kt similarity index 78% rename from features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutEvents.kt rename to features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutEvent.kt index cc16dfdaf1..d19a62768a 100644 --- a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutEvents.kt +++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutEvent.kt @@ -8,7 +8,7 @@ package io.element.android.features.logout.api.direct -sealed interface DirectLogoutEvents { - data class Logout(val ignoreSdkError: Boolean) : DirectLogoutEvents - data object CloseDialogs : DirectLogoutEvents +sealed interface DirectLogoutEvent { + data class Logout(val ignoreSdkError: Boolean) : DirectLogoutEvent + data object CloseDialogs : DirectLogoutEvent } diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutState.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutState.kt index 31e0fdcd5f..75c3392ac1 100644 --- a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutState.kt +++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutState.kt @@ -13,5 +13,5 @@ import io.element.android.libraries.architecture.AsyncAction data class DirectLogoutState( val canDoDirectSignOut: Boolean, val logoutAction: AsyncAction, - val eventSink: (DirectLogoutEvents) -> Unit, + val eventSink: (DirectLogoutEvent) -> Unit, ) diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutStatePreviewParam.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutStatePreviewParam.kt index c208978fd9..936f7d2a58 100644 --- a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutStatePreviewParam.kt +++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutStatePreviewParam.kt @@ -25,7 +25,7 @@ open class DirectLogoutStatePreviewParam : PreviewParameterProvider = AsyncAction.Uninitialized, - eventSink: (DirectLogoutEvents) -> Unit = {}, + eventSink: (DirectLogoutEvent) -> Unit = {}, ) = DirectLogoutState( canDoDirectSignOut = canDoDirectSignOut, logoutAction = logoutAction, diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutView.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutView.kt index a8cad6ffba..901e4e9654 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutView.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutView.kt @@ -11,7 +11,7 @@ package io.element.android.features.logout.impl.direct import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.PreviewParameter import dev.zacsweers.metro.ContributesBinding -import io.element.android.features.logout.api.direct.DirectLogoutEvents +import io.element.android.features.logout.api.direct.DirectLogoutEvent import io.element.android.features.logout.api.direct.DirectLogoutState import io.element.android.features.logout.api.direct.DirectLogoutStatePreviewParam import io.element.android.features.logout.api.direct.DirectLogoutView @@ -28,13 +28,13 @@ class DefaultDirectLogoutView : DirectLogoutView { LogoutActionDialog( state.logoutAction, onConfirmClick = { - eventSink(DirectLogoutEvents.Logout(ignoreSdkError = false)) + eventSink(DirectLogoutEvent.Logout(ignoreSdkError = false)) }, onForceLogoutClick = { - eventSink(DirectLogoutEvents.Logout(ignoreSdkError = true)) + eventSink(DirectLogoutEvent.Logout(ignoreSdkError = true)) }, onDismissDialog = { - eventSink(DirectLogoutEvents.CloseDialogs) + eventSink(DirectLogoutEvent.CloseDialogs) }, ) } diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenter.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenter.kt index 8d8cc9141e..79798dd2ab 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenter.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenter.kt @@ -16,7 +16,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import dev.zacsweers.metro.Inject -import io.element.android.features.logout.api.direct.DirectLogoutEvents +import io.element.android.features.logout.api.direct.DirectLogoutEvent import io.element.android.features.logout.api.direct.DirectLogoutState import io.element.android.features.logout.impl.tools.isBackingUp import io.element.android.libraries.architecture.AsyncAction @@ -48,16 +48,16 @@ class DirectLogoutPresenter( val isLastDevice by encryptionService.isLastDevice.collectAsState() - fun handleEvent(event: DirectLogoutEvents) { + fun handleEvent(event: DirectLogoutEvent) { when (event) { - is DirectLogoutEvents.Logout -> { + is DirectLogoutEvent.Logout -> { if (logoutAction.value.isConfirming() || event.ignoreSdkError) { localCoroutineScope.logout(logoutAction, event.ignoreSdkError) } else { logoutAction.value = AsyncAction.ConfirmingNoParams } } - DirectLogoutEvents.CloseDialogs -> { + DirectLogoutEvent.CloseDialogs -> { logoutAction.value = AsyncAction.Uninitialized } } diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutViewTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutViewTest.kt index db8eed2a54..a413ba4e36 100644 --- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutViewTest.kt +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutViewTest.kt @@ -14,7 +14,7 @@ import androidx.activity.ComponentActivity import androidx.compose.ui.test.AndroidComposeUiTest import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.v2.runAndroidComposeUiTest -import io.element.android.features.logout.api.direct.DirectLogoutEvents +import io.element.android.features.logout.api.direct.DirectLogoutEvent import io.element.android.features.logout.api.direct.DirectLogoutState import io.element.android.features.logout.api.direct.aDirectLogoutState import io.element.android.libraries.architecture.AsyncAction @@ -29,7 +29,7 @@ import org.junit.Test class DefaultDirectLogoutViewTest : RobolectricTest() { @Test fun `clicking on confirm logout sends expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setDefaultDirectLogoutView( state = aDirectLogoutState( logoutAction = AsyncAction.ConfirmingNoParams, @@ -37,12 +37,12 @@ class DefaultDirectLogoutViewTest : RobolectricTest() { ) ) clickOn(CommonStrings.action_signout) - eventsRecorder.assertSingle(DirectLogoutEvents.Logout(false)) + eventsRecorder.assertSingle(DirectLogoutEvent.Logout(false)) } @Test fun `clicking on cancel logout sends expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setDefaultDirectLogoutView( state = aDirectLogoutState( logoutAction = AsyncAction.ConfirmingNoParams, @@ -50,13 +50,13 @@ class DefaultDirectLogoutViewTest : RobolectricTest() { ) ) clickOn(CommonStrings.action_cancel) - eventsRecorder.assertSingle(DirectLogoutEvents.CloseDialogs) + eventsRecorder.assertSingle(DirectLogoutEvent.CloseDialogs) } @Ignore("Pressing back key should dismiss the dialog, and so generate the expected event, but it's not the case.") @Test fun `clicking on back invoke back callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setDefaultDirectLogoutView( state = aDirectLogoutState( logoutAction = AsyncAction.ConfirmingNoParams, @@ -64,12 +64,12 @@ class DefaultDirectLogoutViewTest : RobolectricTest() { ) ) pressBackKey() - eventsRecorder.assertSingle(DirectLogoutEvents.CloseDialogs) + eventsRecorder.assertSingle(DirectLogoutEvent.CloseDialogs) } @Test fun `clicking on confirm after error sends expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setDefaultDirectLogoutView( state = aDirectLogoutState( logoutAction = AsyncAction.Failure(Exception("Error")), @@ -77,12 +77,12 @@ class DefaultDirectLogoutViewTest : RobolectricTest() { ) ) clickOn(CommonStrings.action_signout_anyway) - eventsRecorder.assertSingle(DirectLogoutEvents.Logout(true)) + eventsRecorder.assertSingle(DirectLogoutEvent.Logout(true)) } @Test fun `clicking on cancel after error sends expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setDefaultDirectLogoutView( state = aDirectLogoutState( logoutAction = AsyncAction.Failure(Exception("Error")), @@ -90,7 +90,7 @@ class DefaultDirectLogoutViewTest : RobolectricTest() { ) ) clickOn(CommonStrings.action_cancel) - eventsRecorder.assertSingle(DirectLogoutEvents.CloseDialogs) + eventsRecorder.assertSingle(DirectLogoutEvent.CloseDialogs) } } diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenterTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenterTest.kt index 835ed4e94e..d11c31cf1b 100644 --- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenterTest.kt +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenterTest.kt @@ -13,7 +13,7 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.ReceiveTurbine import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.features.logout.api.direct.DirectLogoutEvents +import io.element.android.features.logout.api.direct.DirectLogoutEvent import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.encryption.BackupUploadState @@ -87,10 +87,10 @@ class DirectLogoutPresenterTest { presenter.present() }.test { val initialState = awaitFirstItem() - initialState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false)) + initialState.eventSink.invoke(DirectLogoutEvent.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams) - initialState.eventSink.invoke(DirectLogoutEvents.CloseDialogs) + initialState.eventSink.invoke(DirectLogoutEvent.CloseDialogs) val finalState = awaitItem() assertThat(finalState.logoutAction).isEqualTo(AsyncAction.Uninitialized) } @@ -103,10 +103,10 @@ class DirectLogoutPresenterTest { presenter.present() }.test { val initialState = awaitFirstItem() - initialState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false)) + initialState.eventSink.invoke(DirectLogoutEvent.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams) - confirmationState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false)) + confirmationState.eventSink.invoke(DirectLogoutEvent.Logout(ignoreSdkError = false)) val loadingState = awaitItem() assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) val successState = awaitItem() @@ -128,15 +128,15 @@ class DirectLogoutPresenterTest { presenter.present() }.test { val initialState = awaitFirstItem() - initialState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false)) + initialState.eventSink.invoke(DirectLogoutEvent.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams) - confirmationState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false)) + confirmationState.eventSink.invoke(DirectLogoutEvent.Logout(ignoreSdkError = false)) val loadingState = awaitItem() assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) val errorState = awaitItem() assertThat(errorState.logoutAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) - errorState.eventSink.invoke(DirectLogoutEvents.CloseDialogs) + errorState.eventSink.invoke(DirectLogoutEvent.CloseDialogs) val finalState = awaitItem() assertThat(finalState.logoutAction).isEqualTo(AsyncAction.Uninitialized) } @@ -158,15 +158,15 @@ class DirectLogoutPresenterTest { presenter.present() }.test { val initialState = awaitFirstItem() - initialState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false)) + initialState.eventSink.invoke(DirectLogoutEvent.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams) - confirmationState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = false)) + confirmationState.eventSink.invoke(DirectLogoutEvent.Logout(ignoreSdkError = false)) val loadingState = awaitItem() assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) val errorState = awaitItem() assertThat(errorState.logoutAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) - errorState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = true)) + errorState.eventSink.invoke(DirectLogoutEvent.Logout(ignoreSdkError = true)) val loadingState2 = awaitItem() assertThat(loadingState2.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) val successState = awaitItem() diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt index 8ee72fba6d..4fdac07550 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt @@ -19,7 +19,7 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.compound.theme.ElementTheme -import io.element.android.features.logout.api.direct.DirectLogoutEvents +import io.element.android.features.logout.api.direct.DirectLogoutEvent import io.element.android.features.logout.api.direct.DirectLogoutView import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.architecture.callback @@ -96,7 +96,7 @@ class PreferencesRootNode( onOpenBlockedUsers = callback::navigateToBlockedUsers, onSignOutClick = { if (state.directLogoutState.canDoDirectSignOut) { - state.directLogoutState.eventSink(DirectLogoutEvents.Logout(ignoreSdkError = false)) + state.directLogoutState.eventSink(DirectLogoutEvent.Logout(ignoreSdkError = false)) } else { callback.startSignOutFlow() } From 116897d64c32454b16b382e2911426c4a3f4656a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:05:38 +0200 Subject: [PATCH 036/112] `LogoutEvents` -> `LogoutEvent` --- .../impl/{LogoutEvents.kt => LogoutEvent.kt} | 6 ++-- .../features/logout/impl/LogoutPresenter.kt | 6 ++-- .../features/logout/impl/LogoutState.kt | 2 +- .../logout/impl/LogoutStatePreviewParam.kt | 2 +- .../features/logout/impl/LogoutView.kt | 8 +++--- .../logout/impl/LogoutPresenterTest.kt | 20 ++++++------- .../features/logout/impl/LogoutViewTest.kt | 28 +++++++++---------- 7 files changed, 36 insertions(+), 36 deletions(-) rename features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/{LogoutEvents.kt => LogoutEvent.kt} (81%) diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutEvents.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutEvent.kt similarity index 81% rename from features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutEvents.kt rename to features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutEvent.kt index 0822444bea..e23338460c 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutEvents.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutEvent.kt @@ -8,7 +8,7 @@ package io.element.android.features.logout.impl -sealed interface LogoutEvents { - data class Logout(val ignoreSdkError: Boolean) : LogoutEvents - data object CloseDialogs : LogoutEvents +sealed interface LogoutEvent { + data class Logout(val ignoreSdkError: Boolean) : LogoutEvent + data object CloseDialogs : LogoutEvent } diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt index 8176ed3d28..4ad8a6c78e 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt @@ -74,16 +74,16 @@ class LogoutPresenter( } } - fun handleEvent(event: LogoutEvents) { + fun handleEvent(event: LogoutEvent) { when (event) { - is LogoutEvents.Logout -> { + is LogoutEvent.Logout -> { if (logoutAction.value.isConfirming() || event.ignoreSdkError) { localCoroutineScope.logout(logoutAction, event.ignoreSdkError) } else { logoutAction.value = AsyncAction.ConfirmingNoParams } } - LogoutEvents.CloseDialogs -> { + LogoutEvent.CloseDialogs -> { logoutAction.value = AsyncAction.Uninitialized } } diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutState.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutState.kt index 9240edc61d..6233c1cb23 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutState.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutState.kt @@ -21,5 +21,5 @@ data class LogoutState( val backupUploadState: BackupUploadState, val waitingForALongTime: Boolean, val logoutAction: AsyncAction, - val eventSink: (LogoutEvents) -> Unit, + val eventSink: (LogoutEvent) -> Unit, ) diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStatePreviewParam.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStatePreviewParam.kt index 7bd55faa2c..e2d948a7f3 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStatePreviewParam.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutStatePreviewParam.kt @@ -50,7 +50,7 @@ fun aLogoutState( backupUploadState: BackupUploadState = BackupUploadState.Unknown, waitingForALongTime: Boolean = false, logoutAction: AsyncAction = AsyncAction.Uninitialized, - eventSink: (LogoutEvents) -> Unit = {}, + eventSink: (LogoutEvent) -> Unit = {}, ) = LogoutState( isLastDevice = isLastDevice, backupState = backupState, diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt index befabe7421..90a502fc16 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt @@ -61,7 +61,7 @@ fun LogoutView( state = state, onChangeRecoveryKeyClick = onChangeRecoveryKeyClick, onLogoutClick = { - eventSink(LogoutEvents.Logout(ignoreSdkError = false)) + eventSink(LogoutEvent.Logout(ignoreSdkError = false)) } ) }, @@ -72,13 +72,13 @@ fun LogoutView( LogoutActionDialog( state.logoutAction, onConfirmClick = { - eventSink(LogoutEvents.Logout(ignoreSdkError = false)) + eventSink(LogoutEvent.Logout(ignoreSdkError = false)) }, onForceLogoutClick = { - eventSink(LogoutEvents.Logout(ignoreSdkError = true)) + eventSink(LogoutEvent.Logout(ignoreSdkError = true)) }, onDismissDialog = { - eventSink(LogoutEvents.CloseDialogs) + eventSink(LogoutEvent.CloseDialogs) }, ) } diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt index 274234a338..5b14bc5cc3 100644 --- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt @@ -139,10 +139,10 @@ class LogoutPresenterTest { presenter.present() }.test { val initialState = awaitFirstItem() - initialState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false)) + initialState.eventSink.invoke(LogoutEvent.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams) - initialState.eventSink.invoke(LogoutEvents.CloseDialogs) + initialState.eventSink.invoke(LogoutEvent.CloseDialogs) val finalState = awaitItem() assertThat(finalState.logoutAction).isEqualTo(AsyncAction.Uninitialized) } @@ -157,10 +157,10 @@ class LogoutPresenterTest { presenter.present() }.test { val initialState = awaitFirstItem() - initialState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false)) + initialState.eventSink.invoke(LogoutEvent.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams) - confirmationState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false)) + confirmationState.eventSink.invoke(LogoutEvent.Logout(ignoreSdkError = false)) val loadingState = awaitItem() assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) val successState = awaitItem() @@ -184,15 +184,15 @@ class LogoutPresenterTest { presenter.present() }.test { val initialState = awaitFirstItem() - initialState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false)) + initialState.eventSink.invoke(LogoutEvent.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams) - confirmationState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false)) + confirmationState.eventSink.invoke(LogoutEvent.Logout(ignoreSdkError = false)) val loadingState = awaitItem() assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) val errorState = awaitItem() assertThat(errorState.logoutAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) - errorState.eventSink.invoke(LogoutEvents.CloseDialogs) + errorState.eventSink.invoke(LogoutEvent.CloseDialogs) val finalState = awaitItem() assertThat(finalState.logoutAction).isEqualTo(AsyncAction.Uninitialized) } @@ -214,15 +214,15 @@ class LogoutPresenterTest { presenter.present() }.test { val initialState = awaitFirstItem() - initialState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false)) + initialState.eventSink.invoke(LogoutEvent.Logout(ignoreSdkError = false)) val confirmationState = awaitItem() assertThat(confirmationState.logoutAction).isEqualTo(AsyncAction.ConfirmingNoParams) - confirmationState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = false)) + confirmationState.eventSink.invoke(LogoutEvent.Logout(ignoreSdkError = false)) val loadingState = awaitItem() assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) val errorState = awaitItem() assertThat(errorState.logoutAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) - errorState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = true)) + errorState.eventSink.invoke(LogoutEvent.Logout(ignoreSdkError = true)) val loadingState2 = awaitItem() assertThat(loadingState2.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) val successState = awaitItem() diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutViewTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutViewTest.kt index 3fbae4bbc5..f69ec8f0ca 100644 --- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutViewTest.kt +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutViewTest.kt @@ -28,20 +28,20 @@ import org.junit.Test class LogoutViewTest : RobolectricTest() { @Test - fun `clicking on logout sends a LogoutEvents`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + fun `clicking on logout sends a LogoutEvent`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() setLogoutView( aLogoutState( eventSink = eventsRecorder ), ) clickOn(CommonStrings.action_signout) - eventsRecorder.assertSingle(LogoutEvents.Logout(false)) + eventsRecorder.assertSingle(LogoutEvent.Logout(false)) } @Test - fun `confirming logout sends a LogoutEvents`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + fun `confirming logout sends a LogoutEvent`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() setLogoutView( aLogoutState( logoutAction = AsyncAction.ConfirmingNoParams, @@ -49,12 +49,12 @@ class LogoutViewTest : RobolectricTest() { ), ) pressTag(TestTags.dialogPositive.value) - eventsRecorder.assertSingle(LogoutEvents.Logout(false)) + eventsRecorder.assertSingle(LogoutEvent.Logout(false)) } @Test fun `clicking on back invoke back callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> setLogoutView( aLogoutState( @@ -67,8 +67,8 @@ class LogoutViewTest : RobolectricTest() { } @Test - fun `clicking on confirm after error sends a LogoutEvents`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + fun `clicking on confirm after error sends a LogoutEvent`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() setLogoutView( aLogoutState( logoutAction = AsyncAction.Failure(Exception("Failed to logout")), @@ -76,12 +76,12 @@ class LogoutViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_signout_anyway) - eventsRecorder.assertSingle(LogoutEvents.Logout(true)) + eventsRecorder.assertSingle(LogoutEvent.Logout(true)) } @Test - fun `clicking on cancel after error sends a LogoutEvents`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + fun `clicking on cancel after error sends a LogoutEvent`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() setLogoutView( aLogoutState( logoutAction = AsyncAction.Failure(Exception("Failed to logout")), @@ -89,12 +89,12 @@ class LogoutViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_cancel) - eventsRecorder.assertSingle(LogoutEvents.CloseDialogs) + eventsRecorder.assertSingle(LogoutEvent.CloseDialogs) } @Test fun `last session setting button invoke onChangeRecoveryKeyClicked`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> setLogoutView( aLogoutState( From d42a93100da8c13a8999cd25ca9432062d186e1e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:06:20 +0200 Subject: [PATCH 037/112] `ThreadsListEvents` -> `ThreadsListEvent` --- .../messages/impl/threads/list/ThreadsListPresenter.kt | 10 +++++----- .../messages/impl/threads/list/ThreadsListView.kt | 2 +- .../messages/impl/threads/ThreadsListPresenterTest.kt | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListPresenter.kt index ba10df46f2..ba1366df44 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListPresenter.kt @@ -104,9 +104,9 @@ class ThreadsListPresenter( derivedStateOf { roomInfo.heroes.map { it.getAvatarData(AvatarSize.CurrentUserTopBar) }.toImmutableList() } } - fun handleEvent(event: ThreadsListEvents) { + fun handleEvent(event: ThreadsListEvent) { when (event) { - ThreadsListEvents.Paginate -> if ((paginationStatus as? ThreadListPaginationStatus.Idle)?.hasMoreToLoad == true) { + ThreadsListEvent.Paginate -> if ((paginationStatus as? ThreadListPaginationStatus.Idle)?.hasMoreToLoad == true) { coroutineScope.launch { Timber.d("Paginating thread list: $paginationStatus") threadsListService.paginate() @@ -137,9 +137,9 @@ data class ThreadsListState( val isRoomTombstoned: Boolean, val heroes: ImmutableList, val threads: ImmutableList, - val eventSink: (ThreadsListEvents) -> Unit, + val eventSink: (ThreadsListEvent) -> Unit, ) -sealed interface ThreadsListEvents { - data object Paginate : ThreadsListEvents +sealed interface ThreadsListEvent { + data object Paginate : ThreadsListEvent } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt index 6480dacfcf..c38da19a67 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt @@ -156,7 +156,7 @@ fun ThreadsListView( } ScrollHelper(lazyListState) { - state.eventSink(ThreadsListEvents.Paginate) + state.eventSink(ThreadsListEvent.Paginate) } } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/threads/ThreadsListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/threads/ThreadsListPresenterTest.kt index 9f8d210a16..fab7602906 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/threads/ThreadsListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/threads/ThreadsListPresenterTest.kt @@ -10,7 +10,7 @@ package io.element.android.features.messages.impl.threads import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.fixtures.aTimelineItemContentFactory import io.element.android.features.messages.impl.messagesummary.FakeMessageSummaryFormatter -import io.element.android.features.messages.impl.threads.list.ThreadsListEvents +import io.element.android.features.messages.impl.threads.list.ThreadsListEvent import io.element.android.features.messages.impl.threads.list.ThreadsListPresenter import io.element.android.features.messages.impl.threads.list.aThreadListItem import io.element.android.libraries.dateformatter.test.FakeDateFormatter @@ -48,7 +48,7 @@ class ThreadsListPresenterTest { // Pagination is automatically triggered on start, so we should have one call to paginate already paginateRecorder.assertions().isCalledOnce() - initialItem.eventSink(ThreadsListEvents.Paginate) + initialItem.eventSink(ThreadsListEvent.Paginate) // Simulate a pagination result threadsListService.emit(listOf(aThreadListItem())) From 87af3b85b3d897c7f509cb771a9dad262df69e94 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:06:58 +0200 Subject: [PATCH 038/112] `PollHistoryEvents` -> `PollHistoryEvent` --- ...llHistoryEvents.kt => PollHistoryEvent.kt} | 10 +++++----- .../poll/impl/history/PollHistoryPresenter.kt | 10 +++++----- .../poll/impl/history/PollHistoryState.kt | 2 +- .../history/PollHistoryStatePreviewParam.kt | 2 +- .../poll/impl/history/PollHistoryView.kt | 8 ++++---- .../impl/history/PollHistoryPresenterTest.kt | 10 +++++----- .../poll/impl/history/PollHistoryViewTest.kt | 20 +++++++++---------- 7 files changed, 31 insertions(+), 31 deletions(-) rename features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/{PollHistoryEvents.kt => PollHistoryEvent.kt} (81%) diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryEvents.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryEvent.kt similarity index 81% rename from features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryEvents.kt rename to features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryEvent.kt index 14ce13d67a..9d0d699d89 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryEvents.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryEvent.kt @@ -11,9 +11,9 @@ package io.element.android.features.poll.impl.history import io.element.android.features.poll.impl.history.model.PollHistoryFilter import io.element.android.libraries.matrix.api.core.EventId -sealed interface PollHistoryEvents { - data object LoadMore : PollHistoryEvents - data class SelectPollAnswer(val pollStartId: EventId, val answerId: String) : PollHistoryEvents - data class EndPoll(val pollStartId: EventId) : PollHistoryEvents - data class SelectFilter(val filter: PollHistoryFilter) : PollHistoryEvents +sealed interface PollHistoryEvent { + data object LoadMore : PollHistoryEvent + data class SelectPollAnswer(val pollStartId: EventId, val answerId: String) : PollHistoryEvent + data class EndPoll(val pollStartId: EventId) : PollHistoryEvent + data class SelectFilter(val filter: PollHistoryFilter) : PollHistoryEvent } diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenter.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenter.kt index 434e2ed18c..74a511da45 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenter.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenter.kt @@ -63,22 +63,22 @@ class PollHistoryPresenter( } } val coroutineScope = rememberCoroutineScope() - fun handleEvent(event: PollHistoryEvents) { + fun handleEvent(event: PollHistoryEvent) { when (event) { - is PollHistoryEvents.LoadMore -> { + is PollHistoryEvent.LoadMore -> { coroutineScope.loadMore(timeline) } - is PollHistoryEvents.SelectPollAnswer -> sessionCoroutineScope.launch { + is PollHistoryEvent.SelectPollAnswer -> sessionCoroutineScope.launch { sendPollResponseAction.execute( timeline = timeline, pollStartId = event.pollStartId, answerId = event.answerId ) } - is PollHistoryEvents.EndPoll -> sessionCoroutineScope.launch { + is PollHistoryEvent.EndPoll -> sessionCoroutineScope.launch { endPollAction.execute(timeline = timeline, pollStartId = event.pollStartId) } - is PollHistoryEvents.SelectFilter -> { + is PollHistoryEvent.SelectFilter -> { activeFilter = event.filter } } diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryState.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryState.kt index bddfd153f6..67225e85c2 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryState.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryState.kt @@ -18,7 +18,7 @@ data class PollHistoryState( val hasMoreToLoad: Boolean, val activeFilter: PollHistoryFilter, val pollHistoryItems: PollHistoryItems, - val eventSink: (PollHistoryEvents) -> Unit, + val eventSink: (PollHistoryEvent) -> Unit, ) { fun pollHistoryForFilter(filter: PollHistoryFilter): ImmutableList { return when (filter) { diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStatePreviewParam.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStatePreviewParam.kt index ff07eaee37..40144f261e 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStatePreviewParam.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStatePreviewParam.kt @@ -48,7 +48,7 @@ internal fun aPollHistoryState( currentItems: List = listOf( aPollHistoryItem(), ), - eventSink: (PollHistoryEvents) -> Unit = {}, + eventSink: (PollHistoryEvent) -> Unit = {}, ) = PollHistoryState( isLoading = isLoading, hasMoreToLoad = hasMoreToLoad, diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryView.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryView.kt index 01b475593d..61c7809f49 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryView.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryView.kt @@ -62,15 +62,15 @@ fun PollHistoryView( modifier: Modifier = Modifier, ) { fun onLoadMore() { - state.eventSink(PollHistoryEvents.LoadMore) + state.eventSink(PollHistoryEvent.LoadMore) } fun onSelectAnswer(pollStartId: EventId, answerId: String) { - state.eventSink(PollHistoryEvents.SelectPollAnswer(pollStartId, answerId)) + state.eventSink(PollHistoryEvent.SelectPollAnswer(pollStartId, answerId)) } fun onEndPoll(pollStartId: EventId) { - state.eventSink(PollHistoryEvents.EndPoll(pollStartId)) + state.eventSink(PollHistoryEvent.EndPoll(pollStartId)) } Scaffold( @@ -97,7 +97,7 @@ fun PollHistoryView( } PollHistoryFilterButtons( activeFilter = state.activeFilter, - onSelectFilter = { state.eventSink(PollHistoryEvents.SelectFilter(it)) }, + onSelectFilter = { state.eventSink(PollHistoryEvent.SelectFilter(it)) }, modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 8.dp), diff --git a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenterTest.kt b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenterTest.kt index 8d4cedc6a8..1342f89c60 100644 --- a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenterTest.kt +++ b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenterTest.kt @@ -88,12 +88,12 @@ class PollHistoryPresenterTest { }.test { awaitItem().also { state -> assertThat(state.activeFilter).isEqualTo(PollHistoryFilter.ONGOING) - state.eventSink(PollHistoryEvents.SelectFilter(PollHistoryFilter.PAST)) + state.eventSink(PollHistoryEvent.SelectFilter(PollHistoryFilter.PAST)) } skipItems(1) awaitItem().also { state -> assertThat(state.activeFilter).isEqualTo(PollHistoryFilter.PAST) - state.eventSink(PollHistoryEvents.SelectFilter(PollHistoryFilter.ONGOING)) + state.eventSink(PollHistoryEvent.SelectFilter(PollHistoryFilter.ONGOING)) } awaitItem().also { state -> assertThat(state.activeFilter).isEqualTo(PollHistoryFilter.ONGOING) @@ -114,10 +114,10 @@ class PollHistoryPresenterTest { presenter.present() }.test { val state = awaitItem() - state.eventSink(PollHistoryEvents.EndPoll(AN_EVENT_ID)) + state.eventSink(PollHistoryEvent.EndPoll(AN_EVENT_ID)) runCurrent() endPollAction.verifyExecutionCount(1) - state.eventSink(PollHistoryEvents.SelectPollAnswer(AN_EVENT_ID, "answer")) + state.eventSink(PollHistoryEvent.SelectPollAnswer(AN_EVENT_ID, "answer")) runCurrent() sendPollResponseAction.verifyExecutionCount(1) cancelAndConsumeRemainingEvents() @@ -139,7 +139,7 @@ class PollHistoryPresenterTest { skipItems(1) val loadedState = awaitItem() assertThat(loadedState.isLoading).isFalse() - loadedState.eventSink(PollHistoryEvents.LoadMore) + loadedState.eventSink(PollHistoryEvent.LoadMore) backwardPaginationStatus.getAndUpdate { it.copy(isPaginating = true) } awaitItem().also { state -> assertThat(state.isLoading).isTrue() diff --git a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/PollHistoryViewTest.kt b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/PollHistoryViewTest.kt index ee6ad69ba6..1b74118734 100644 --- a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/PollHistoryViewTest.kt +++ b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/PollHistoryViewTest.kt @@ -35,7 +35,7 @@ import org.robolectric.annotation.Config class PollHistoryViewTest : RobolectricTest() { @Test fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setPollHistoryViewView( aPollHistoryState( @@ -50,7 +50,7 @@ class PollHistoryViewTest : RobolectricTest() { @Config(qualifiers = "h1024dp") @Test fun `clicking on edit poll invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) val eventId = EventId("\$anEventId") val state = aPollHistoryState( currentItems = listOf( @@ -76,7 +76,7 @@ class PollHistoryViewTest : RobolectricTest() { @Config(qualifiers = "h1024dp") @Test fun `clicking on poll end emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val eventId = EventId("\$anEventId") val state = aPollHistoryState( currentItems = listOf( @@ -102,14 +102,14 @@ class PollHistoryViewTest : RobolectricTest() { eventsRecorder.assertEmpty() clickOn(CommonStrings.action_ok) eventsRecorder.assertSingle( - PollHistoryEvents.EndPoll(eventId) + PollHistoryEvent.EndPoll(eventId) ) } @Config(qualifiers = "h1024dp") @Test fun `clicking on poll answer emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val eventId = EventId("\$anEventId") val state = aPollHistoryState( currentItems = listOf( @@ -133,13 +133,13 @@ class PollHistoryViewTest : RobolectricTest() { useUnmergedTree = true, ).performClick() eventsRecorder.assertSingle( - PollHistoryEvents.SelectPollAnswer(eventId, answer.id) + PollHistoryEvent.SelectPollAnswer(eventId, answer.id) ) } @Test fun `clicking on past tab emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setPollHistoryViewView( aPollHistoryState( eventSink = eventsRecorder @@ -147,14 +147,14 @@ class PollHistoryViewTest : RobolectricTest() { ) clickOn(R.string.screen_polls_history_filter_past) eventsRecorder.assertSingle( - PollHistoryEvents.SelectFilter(filter = PollHistoryFilter.PAST) + PollHistoryEvent.SelectFilter(filter = PollHistoryFilter.PAST) ) } @Config(qualifiers = "h1024dp") @Test fun `clicking on load more emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setPollHistoryViewView( aPollHistoryState( hasMoreToLoad = true, @@ -163,7 +163,7 @@ class PollHistoryViewTest : RobolectricTest() { ) clickOn(CommonStrings.action_load_more) eventsRecorder.assertSingle( - PollHistoryEvents.LoadMore + PollHistoryEvent.LoadMore ) } } From e6be199d4a4633966aa150107f7ca979b9ecffa0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:07:35 +0200 Subject: [PATCH 039/112] `AdvancedSettingsEvents` -> `AdvancedSettingsEvent` --- ...ingsEvents.kt => AdvancedSettingsEvent.kt} | 20 ++++----- .../advanced/AdvancedSettingsPresenter.kt | 20 ++++----- .../impl/advanced/AdvancedSettingsState.kt | 2 +- .../AdvancedSettingsStatePreviewParam.kt | 2 +- .../impl/advanced/AdvancedSettingsView.kt | 22 +++++----- .../advanced/AdvancedSettingsPresenterTest.kt | 36 ++++++++-------- .../impl/advanced/AdvancedSettingsViewTest.kt | 42 +++++++++---------- 7 files changed, 72 insertions(+), 72 deletions(-) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/{AdvancedSettingsEvents.kt => AdvancedSettingsEvent.kt} (80%) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvent.kt similarity index 80% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvent.kt index ef27c499af..f2e6537d9d 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvent.kt @@ -11,14 +11,14 @@ package io.element.android.features.preferences.impl.advanced import io.element.android.libraries.matrix.api.media.MediaPreviewValue import io.element.android.libraries.preferences.api.store.VideoCompressionPreset -sealed interface AdvancedSettingsEvents { - data class SetDeveloperModeEnabled(val enabled: Boolean) : AdvancedSettingsEvents - data class SetSharePresenceEnabled(val enabled: Boolean) : AdvancedSettingsEvents - data class SetCompressMedia(val compress: Boolean) : AdvancedSettingsEvents - data class SetCompressImages(val compress: Boolean) : AdvancedSettingsEvents - data class SetVideoUploadQuality(val videoPreset: VideoCompressionPreset) : AdvancedSettingsEvents - data class SetTheme(val theme: ThemeOption) : AdvancedSettingsEvents - data class SetTimelineMediaPreviewValue(val value: MediaPreviewValue) : AdvancedSettingsEvents - data class SetHideInviteAvatars(val value: Boolean) : AdvancedSettingsEvents - data class SetLiveLocationMinimumDistanceUpdate(val value: Int) : AdvancedSettingsEvents +sealed interface AdvancedSettingsEvent { + data class SetDeveloperModeEnabled(val enabled: Boolean) : AdvancedSettingsEvent + data class SetSharePresenceEnabled(val enabled: Boolean) : AdvancedSettingsEvent + data class SetCompressMedia(val compress: Boolean) : AdvancedSettingsEvent + data class SetCompressImages(val compress: Boolean) : AdvancedSettingsEvent + data class SetVideoUploadQuality(val videoPreset: VideoCompressionPreset) : AdvancedSettingsEvent + data class SetTheme(val theme: ThemeOption) : AdvancedSettingsEvent + data class SetTimelineMediaPreviewValue(val value: MediaPreviewValue) : AdvancedSettingsEvent + data class SetHideInviteAvatars(val value: Boolean) : AdvancedSettingsEvent + data class SetLiveLocationMinimumDistanceUpdate(val value: Int) : AdvancedSettingsEvent } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt index 45c136ab62..3ebf1f5140 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt @@ -100,18 +100,18 @@ class AdvancedSettingsPresenter( }.collect() } - fun handleEvent(event: AdvancedSettingsEvents) { + fun handleEvent(event: AdvancedSettingsEvent) { when (event) { - is AdvancedSettingsEvents.SetDeveloperModeEnabled -> sessionCoroutineScope.launch { + is AdvancedSettingsEvent.SetDeveloperModeEnabled -> sessionCoroutineScope.launch { appPreferencesStore.setDeveloperModeEnabled(event.enabled) } - is AdvancedSettingsEvents.SetSharePresenceEnabled -> sessionCoroutineScope.launch { + is AdvancedSettingsEvent.SetSharePresenceEnabled -> sessionCoroutineScope.launch { sessionPreferencesStore.setSharePresence(event.enabled) } - is AdvancedSettingsEvents.SetCompressMedia -> sessionCoroutineScope.launch { + is AdvancedSettingsEvent.SetCompressMedia -> sessionCoroutineScope.launch { sessionPreferencesStore.setOptimizeImages(event.compress) } - is AdvancedSettingsEvents.SetTheme -> sessionCoroutineScope.launch { + is AdvancedSettingsEvent.SetTheme -> sessionCoroutineScope.launch { when (event.theme) { ThemeOption.System -> appPreferencesStore.setTheme(Theme.System.name) ThemeOption.Dark -> appPreferencesStore.setTheme(Theme.Dark.name) @@ -119,15 +119,15 @@ class AdvancedSettingsPresenter( ThemeOption.Light -> appPreferencesStore.setTheme(Theme.Light.name) } } - is AdvancedSettingsEvents.SetHideInviteAvatars -> mediaPreviewConfigStateStore.setHideInviteAvatars(event.value) - is AdvancedSettingsEvents.SetTimelineMediaPreviewValue -> mediaPreviewConfigStateStore.setTimelineMediaPreviewValue(event.value) - is AdvancedSettingsEvents.SetLiveLocationMinimumDistanceUpdate -> sessionCoroutineScope.launch { + is AdvancedSettingsEvent.SetHideInviteAvatars -> mediaPreviewConfigStateStore.setHideInviteAvatars(event.value) + is AdvancedSettingsEvent.SetTimelineMediaPreviewValue -> mediaPreviewConfigStateStore.setTimelineMediaPreviewValue(event.value) + is AdvancedSettingsEvent.SetLiveLocationMinimumDistanceUpdate -> sessionCoroutineScope.launch { appPreferencesStore.setLiveLocationMinimumDistanceInMetersUpdate(event.value) } - is AdvancedSettingsEvents.SetCompressImages -> sessionCoroutineScope.launch { + is AdvancedSettingsEvent.SetCompressImages -> sessionCoroutineScope.launch { sessionPreferencesStore.setOptimizeImages(event.compress) } - is AdvancedSettingsEvents.SetVideoUploadQuality -> sessionCoroutineScope.launch { + is AdvancedSettingsEvent.SetVideoUploadQuality -> sessionCoroutineScope.launch { sessionPreferencesStore.setVideoCompressionPreset(event.videoPreset) } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt index 94019c1f01..689075759d 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt @@ -24,7 +24,7 @@ data class AdvancedSettingsState( val availableThemeOptions: ImmutableList, val mediaPreviewConfigState: MediaPreviewConfigState, val liveLocationMinimumDistanceUpdate: Int?, - val eventSink: (AdvancedSettingsEvents) -> Unit + val eventSink: (AdvancedSettingsEvent) -> Unit ) sealed interface MediaOptimizationState { diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStatePreviewParam.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStatePreviewParam.kt index c41df427cc..3b6a323419 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStatePreviewParam.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStatePreviewParam.kt @@ -44,7 +44,7 @@ fun aAdvancedSettingsState( liveLocationMinimumDistanceUpdate: Int? = 50, setTimelineMediaPreviewAction: AsyncAction = AsyncAction.Uninitialized, setHideInviteAvatarsAction: AsyncAction = AsyncAction.Uninitialized, - eventSink: (AdvancedSettingsEvents) -> Unit = {}, + eventSink: (AdvancedSettingsEvent) -> Unit = {}, ) = AdvancedSettingsState( isDeveloperModeEnabled = isDeveloperModeEnabled, isSharePresenceEnabled = isSharePresenceEnabled, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt index de6ce5c9f6..d7b1cfeb17 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt @@ -89,7 +89,7 @@ fun AdvancedSettingsView( selectedOption = state.theme, options = state.availableThemeOptions, onSelectOption = { themeOption -> - state.eventSink(AdvancedSettingsEvents.SetTheme(themeOption)) + state.eventSink(AdvancedSettingsEvent.SetTheme(themeOption)) } ) ListItem( @@ -102,7 +102,7 @@ fun AdvancedSettingsView( trailingContent = ListItemContent.Switch( checked = state.isDeveloperModeEnabled, ), - onClick = { state.eventSink(AdvancedSettingsEvents.SetDeveloperModeEnabled(!state.isDeveloperModeEnabled)) } + onClick = { state.eventSink(AdvancedSettingsEvent.SetDeveloperModeEnabled(!state.isDeveloperModeEnabled)) } ) ListItem( content = { @@ -114,7 +114,7 @@ fun AdvancedSettingsView( trailingContent = ListItemContent.Switch( checked = state.isSharePresenceEnabled, ), - onClick = { state.eventSink(AdvancedSettingsEvents.SetSharePresenceEnabled(!state.isSharePresenceEnabled)) } + onClick = { state.eventSink(AdvancedSettingsEvent.SetSharePresenceEnabled(!state.isSharePresenceEnabled)) } ) val compressImages = state.mediaOptimizationState?.shouldCompressImages @@ -140,7 +140,7 @@ fun AdvancedSettingsView( Interaction.Name.MobileSettingsOptimizeMediaUploadsDisabled } ) - state.eventSink(AdvancedSettingsEvents.SetCompressMedia(newValue)) + state.eventSink(AdvancedSettingsEvent.SetCompressMedia(newValue)) } ) } @@ -164,7 +164,7 @@ fun AdvancedSettingsView( Interaction.Name.MobileSettingsOptimizeMediaUploadsDisabled } ) - state.eventSink(AdvancedSettingsEvents.SetCompressMedia(newValue)) + state.eventSink(AdvancedSettingsEvent.SetCompressMedia(newValue)) } ) @@ -193,7 +193,7 @@ fun AdvancedSettingsView( VideoQualitySelectorDialog( selectedPreset = state.mediaOptimizationState.videoPreset, onSubmit = { preset -> - state.eventSink(AdvancedSettingsEvents.SetVideoUploadQuality(preset)) + state.eventSink(AdvancedSettingsEvent.SetVideoUploadQuality(preset)) displaySelectorDialog = false }, onDismiss = { displaySelectorDialog = false }, @@ -207,7 +207,7 @@ fun AdvancedSettingsView( LiveLocationUpdatesSection( value = state.liveLocationMinimumDistanceUpdate, onSaveValue = { value -> - state.eventSink(AdvancedSettingsEvents.SetLiveLocationMinimumDistanceUpdate(value)) + state.eventSink(AdvancedSettingsEvent.SetLiveLocationMinimumDistanceUpdate(value)) }, onOpenAppPermissionsClick = onOpenAppSettingsClick, ) @@ -286,7 +286,7 @@ private fun ModerationAndSafety( title = stringResource(R.string.screen_advanced_settings_hide_invite_avatars_toggle_title), isChecked = state.mediaPreviewConfigState.hideInviteAvatars, onCheckedChange = { - state.eventSink(AdvancedSettingsEvents.SetHideInviteAvatars(it)) + state.eventSink(AdvancedSettingsEvent.SetHideInviteAvatars(it)) }, enabled = !state.mediaPreviewConfigState.setHideInviteAvatarsAction.isLoading() ) @@ -307,7 +307,7 @@ private fun ModerationAndSafety( compact = true ), onClick = { - state.eventSink(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.Off)) + state.eventSink(AdvancedSettingsEvent.SetTimelineMediaPreviewValue(MediaPreviewValue.Off)) }, enabled = !state.mediaPreviewConfigState.setTimelineMediaPreviewAction.isLoading() ) @@ -318,7 +318,7 @@ private fun ModerationAndSafety( compact = true ), onClick = { - state.eventSink(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.Private)) + state.eventSink(AdvancedSettingsEvent.SetTimelineMediaPreviewValue(MediaPreviewValue.Private)) }, enabled = !state.mediaPreviewConfigState.setTimelineMediaPreviewAction.isLoading() ) @@ -329,7 +329,7 @@ private fun ModerationAndSafety( compact = true ), onClick = { - state.eventSink(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.On)) + state.eventSink(AdvancedSettingsEvent.SetTimelineMediaPreviewValue(MediaPreviewValue.On)) }, enabled = !state.mediaPreviewConfigState.setTimelineMediaPreviewAction.isLoading() ) diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt index 25d7c01778..2ee594e8b9 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt @@ -71,11 +71,11 @@ class AdvancedSettingsPresenterTest { with(awaitItem()) { assertThat(isDeveloperModeEnabled).isFalse() - eventSink(AdvancedSettingsEvents.SetDeveloperModeEnabled(true)) + eventSink(AdvancedSettingsEvent.SetDeveloperModeEnabled(true)) } with(awaitItem()) { assertThat(isDeveloperModeEnabled).isTrue() - eventSink(AdvancedSettingsEvents.SetDeveloperModeEnabled(false)) + eventSink(AdvancedSettingsEvent.SetDeveloperModeEnabled(false)) } with(awaitItem()) { assertThat(isDeveloperModeEnabled).isFalse() @@ -94,11 +94,11 @@ class AdvancedSettingsPresenterTest { with(awaitItem()) { assertThat(isSharePresenceEnabled).isTrue() - eventSink(AdvancedSettingsEvents.SetSharePresenceEnabled(false)) + eventSink(AdvancedSettingsEvent.SetSharePresenceEnabled(false)) } with(awaitItem()) { assertThat(isSharePresenceEnabled).isFalse() - eventSink(AdvancedSettingsEvents.SetSharePresenceEnabled(true)) + eventSink(AdvancedSettingsEvent.SetSharePresenceEnabled(true)) } with(awaitItem()) { assertThat(isSharePresenceEnabled).isTrue() @@ -117,11 +117,11 @@ class AdvancedSettingsPresenterTest { with(awaitItem()) { assertThat((mediaOptimizationState as MediaOptimizationState.AllMedia).isEnabled).isTrue() - eventSink(AdvancedSettingsEvents.SetCompressMedia(false)) + eventSink(AdvancedSettingsEvent.SetCompressMedia(false)) } with(awaitItem()) { assertThat((mediaOptimizationState as MediaOptimizationState.AllMedia).isEnabled).isFalse() - eventSink(AdvancedSettingsEvents.SetCompressMedia(true)) + eventSink(AdvancedSettingsEvent.SetCompressMedia(true)) } with(awaitItem()) { assertThat((mediaOptimizationState as MediaOptimizationState.AllMedia).isEnabled).isTrue() @@ -144,11 +144,11 @@ class AdvancedSettingsPresenterTest { with(awaitItem()) { assertThat((mediaOptimizationState as MediaOptimizationState.Split).compressImages).isTrue() - eventSink(AdvancedSettingsEvents.SetCompressImages(false)) + eventSink(AdvancedSettingsEvent.SetCompressImages(false)) } with(awaitItem()) { assertThat((mediaOptimizationState as MediaOptimizationState.Split).compressImages).isFalse() - eventSink(AdvancedSettingsEvents.SetCompressImages(true)) + eventSink(AdvancedSettingsEvent.SetCompressImages(true)) } with(awaitItem()) { assertThat((mediaOptimizationState as MediaOptimizationState.Split).compressImages).isTrue() @@ -171,11 +171,11 @@ class AdvancedSettingsPresenterTest { with(awaitItem()) { assertThat((mediaOptimizationState as MediaOptimizationState.Split).videoPreset).isEqualTo(VideoCompressionPreset.STANDARD) - eventSink(AdvancedSettingsEvents.SetVideoUploadQuality(VideoCompressionPreset.LOW)) + eventSink(AdvancedSettingsEvent.SetVideoUploadQuality(VideoCompressionPreset.LOW)) } with(awaitItem()) { assertThat((mediaOptimizationState as MediaOptimizationState.Split).videoPreset).isEqualTo(VideoCompressionPreset.LOW) - eventSink(AdvancedSettingsEvents.SetVideoUploadQuality(VideoCompressionPreset.HIGH)) + eventSink(AdvancedSettingsEvent.SetVideoUploadQuality(VideoCompressionPreset.HIGH)) } with(awaitItem()) { assertThat((mediaOptimizationState as MediaOptimizationState.Split).videoPreset).isEqualTo(VideoCompressionPreset.HIGH) @@ -194,15 +194,15 @@ class AdvancedSettingsPresenterTest { with(awaitItem()) { assertThat(theme).isEqualTo(ThemeOption.System) - eventSink(AdvancedSettingsEvents.SetTheme(ThemeOption.Dark)) + eventSink(AdvancedSettingsEvent.SetTheme(ThemeOption.Dark)) } with(awaitItem()) { assertThat(theme).isEqualTo(ThemeOption.Dark) - eventSink(AdvancedSettingsEvents.SetTheme(ThemeOption.Light)) + eventSink(AdvancedSettingsEvent.SetTheme(ThemeOption.Light)) } with(awaitItem()) { assertThat(theme).isEqualTo(ThemeOption.Light) - eventSink(AdvancedSettingsEvents.SetTheme(ThemeOption.System)) + eventSink(AdvancedSettingsEvent.SetTheme(ThemeOption.System)) } with(awaitItem()) { assertThat(theme).isEqualTo(ThemeOption.System) @@ -242,7 +242,7 @@ class AdvancedSettingsPresenterTest { with(awaitItem()) { assertThat(liveLocationMinimumDistanceUpdate).isEqualTo(10) - eventSink(AdvancedSettingsEvents.SetLiveLocationMinimumDistanceUpdate(42)) + eventSink(AdvancedSettingsEvent.SetLiveLocationMinimumDistanceUpdate(42)) } with(awaitItem()) { assertThat(liveLocationMinimumDistanceUpdate).isEqualTo(42) @@ -298,11 +298,11 @@ class AdvancedSettingsPresenterTest { with(awaitItem()) { assertThat(mediaPreviewConfigState.hideInviteAvatars).isFalse() - eventSink(AdvancedSettingsEvents.SetHideInviteAvatars(true)) + eventSink(AdvancedSettingsEvent.SetHideInviteAvatars(true)) } with(awaitItem()) { assertThat(mediaPreviewConfigState.hideInviteAvatars).isTrue() - eventSink(AdvancedSettingsEvents.SetHideInviteAvatars(false)) + eventSink(AdvancedSettingsEvent.SetHideInviteAvatars(false)) } with(awaitItem()) { assertThat(mediaPreviewConfigState.hideInviteAvatars).isFalse() @@ -323,11 +323,11 @@ class AdvancedSettingsPresenterTest { with(awaitItem()) { assertThat(mediaPreviewConfigState.timelineMediaPreviewValue).isEqualTo(MediaPreviewValue.On) - eventSink(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.Off)) + eventSink(AdvancedSettingsEvent.SetTimelineMediaPreviewValue(MediaPreviewValue.Off)) } with(awaitItem()) { assertThat(mediaPreviewConfigState.timelineMediaPreviewValue).isEqualTo(MediaPreviewValue.Off) - eventSink(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.Private)) + eventSink(AdvancedSettingsEvent.SetTimelineMediaPreviewValue(MediaPreviewValue.Private)) } with(awaitItem()) { assertThat(mediaPreviewConfigState.timelineMediaPreviewValue).isEqualTo(MediaPreviewValue.Private) diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsViewTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsViewTest.kt index fa4ecfd829..52af7d5927 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsViewTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsViewTest.kt @@ -39,7 +39,7 @@ import org.robolectric.annotation.Config class AdvancedSettingsViewTest : RobolectricTest() { @Test fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setAdvancedSettingsView( state = aAdvancedSettingsState( @@ -53,7 +53,7 @@ class AdvancedSettingsViewTest : RobolectricTest() { @Test fun `clicking on other theme emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, @@ -61,7 +61,7 @@ class AdvancedSettingsViewTest : RobolectricTest() { ) clickOn(CommonStrings.common_appearance) clickOn(R.string.theme_dark) - eventsRecorder.assertSingle(AdvancedSettingsEvents.SetTheme(ThemeOption.Dark)) + eventsRecorder.assertSingle(AdvancedSettingsEvent.SetTheme(ThemeOption.Dark)) } @Test @@ -91,31 +91,31 @@ class AdvancedSettingsViewTest : RobolectricTest() { @Test fun `clicking on View source emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, ), ) clickOn(CommonStrings.action_view_source) - eventsRecorder.assertSingle(AdvancedSettingsEvents.SetDeveloperModeEnabled(true)) + eventsRecorder.assertSingle(AdvancedSettingsEvent.SetDeveloperModeEnabled(true)) } @Test fun `clicking on Share presence emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, ), ) clickOn(R.string.screen_advanced_settings_share_presence) - eventsRecorder.assertSingle(AdvancedSettingsEvents.SetSharePresenceEnabled(true)) + eventsRecorder.assertSingle(AdvancedSettingsEvent.SetSharePresenceEnabled(true)) } @Test fun `clicking on media to enable compression emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val analyticsService = FakeAnalyticsService() setAdvancedSettingsView( state = aAdvancedSettingsState( @@ -124,7 +124,7 @@ class AdvancedSettingsViewTest : RobolectricTest() { analyticsService = analyticsService ) clickOn(R.string.screen_advanced_settings_media_compression_description) - eventsRecorder.assertSingle(AdvancedSettingsEvents.SetCompressMedia(true)) + eventsRecorder.assertSingle(AdvancedSettingsEvent.SetCompressMedia(true)) assertThat(analyticsService.capturedEvents).isEqualTo( listOf( Interaction( @@ -136,7 +136,7 @@ class AdvancedSettingsViewTest : RobolectricTest() { @Test fun `clicking on media to disable compression emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val analyticsService = FakeAnalyticsService() setAdvancedSettingsView( state = aAdvancedSettingsState( @@ -146,7 +146,7 @@ class AdvancedSettingsViewTest : RobolectricTest() { analyticsService = analyticsService ) clickOn(R.string.screen_advanced_settings_media_compression_description) - eventsRecorder.assertSingle(AdvancedSettingsEvents.SetCompressMedia(false)) + eventsRecorder.assertSingle(AdvancedSettingsEvent.SetCompressMedia(false)) assertThat(analyticsService.capturedEvents).isEqualTo( listOf( Interaction( @@ -159,7 +159,7 @@ class AdvancedSettingsViewTest : RobolectricTest() { @Test @Config(qualifiers = "h1080dp") fun `clicking on hide invite avatars emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, @@ -167,13 +167,13 @@ class AdvancedSettingsViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_advanced_settings_hide_invite_avatars_toggle_title) - eventsRecorder.assertSingle(AdvancedSettingsEvents.SetHideInviteAvatars(true)) + eventsRecorder.assertSingle(AdvancedSettingsEvent.SetHideInviteAvatars(true)) } @Test @Config(qualifiers = "h1080dp") fun `clicking on timeline media preview always hide emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, @@ -181,13 +181,13 @@ class AdvancedSettingsViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_advanced_settings_show_media_timeline_always_hide) - eventsRecorder.assertSingle(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.Off)) + eventsRecorder.assertSingle(AdvancedSettingsEvent.SetTimelineMediaPreviewValue(MediaPreviewValue.Off)) } @Test @Config(qualifiers = "h1080dp") fun `clicking on timeline media preview private rooms emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, @@ -195,13 +195,13 @@ class AdvancedSettingsViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_advanced_settings_show_media_timeline_private_rooms) - eventsRecorder.assertSingle(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.Private)) + eventsRecorder.assertSingle(AdvancedSettingsEvent.SetTimelineMediaPreviewValue(MediaPreviewValue.Private)) } @Test @Config(qualifiers = "h1080dp") fun `clicking on timeline media preview always show emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, @@ -209,13 +209,13 @@ class AdvancedSettingsViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_advanced_settings_show_media_timeline_always_show) - eventsRecorder.assertSingle(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.On)) + eventsRecorder.assertSingle(AdvancedSettingsEvent.SetTimelineMediaPreviewValue(MediaPreviewValue.On)) } @Test @Config(qualifiers = "h1080dp") fun `hide invite avatars toggle is disabled when action is loading`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, @@ -230,7 +230,7 @@ class AdvancedSettingsViewTest : RobolectricTest() { @Test @Config(qualifiers = "h1080dp") fun `timeline media preview options are disabled when action is loading`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, From d8e70317096c9a0939ce662a756ef8beae2d16e2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:08:17 +0200 Subject: [PATCH 040/112] `BlockedUsersEvents` -> `BlockedUsersEvent` --- ...kedUsersEvents.kt => BlockedUsersEvent.kt} | 10 ++++----- .../blockedusers/BlockedUsersPresenter.kt | 10 ++++----- .../impl/blockedusers/BlockedUsersState.kt | 2 +- .../BlockedUsersStatePreviewParam.kt | 2 +- .../impl/blockedusers/BlockedUsersView.kt | 8 +++---- .../impl/blockedusers/BlockedUserViewTest.kt | 22 +++++++++---------- .../blockedusers/BlockedUsersPresenterTest.kt | 16 +++++++------- 7 files changed, 35 insertions(+), 35 deletions(-) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/{BlockedUsersEvents.kt => BlockedUsersEvent.kt} (67%) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersEvent.kt similarity index 67% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersEvents.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersEvent.kt index 31e516e730..42fdff63eb 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersEvent.kt @@ -10,9 +10,9 @@ package io.element.android.features.preferences.impl.blockedusers import io.element.android.libraries.matrix.api.core.UserId -sealed interface BlockedUsersEvents { - data class Unblock(val userId: UserId) : BlockedUsersEvents - data class CopyToClipboard(val userId: UserId) : BlockedUsersEvents - data object ConfirmUnblock : BlockedUsersEvents - data object Cancel : BlockedUsersEvents +sealed interface BlockedUsersEvent { + data class Unblock(val userId: UserId) : BlockedUsersEvent + data class CopyToClipboard(val userId: UserId) : BlockedUsersEvent + data object ConfirmUnblock : BlockedUsersEvent + data object Cancel : BlockedUsersEvent } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt index 2944089fa1..8f20694d81 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt @@ -74,23 +74,23 @@ class BlockedUsersPresenter( val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() - fun handleEvent(event: BlockedUsersEvents) { + fun handleEvent(event: BlockedUsersEvent) { when (event) { - is BlockedUsersEvents.Unblock -> { + is BlockedUsersEvent.Unblock -> { pendingUserToUnblock = event.userId unblockUserAction.value = AsyncAction.ConfirmingNoParams } - is BlockedUsersEvents.CopyToClipboard -> { + is BlockedUsersEvent.CopyToClipboard -> { clipboardHelper.copyPlainText(event.userId.value) snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard)) } - BlockedUsersEvents.ConfirmUnblock -> { + BlockedUsersEvent.ConfirmUnblock -> { pendingUserToUnblock?.let { coroutineScope.unblockUser(it, unblockUserAction) pendingUserToUnblock = null } } - BlockedUsersEvents.Cancel -> { + BlockedUsersEvent.Cancel -> { pendingUserToUnblock = null unblockUserAction.value = AsyncAction.Uninitialized } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersState.kt index e4b07386c7..d62ab03bff 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersState.kt @@ -17,5 +17,5 @@ data class BlockedUsersState( val blockedUsers: ImmutableList, val unblockUserAction: AsyncAction, val snackbarMessage: SnackbarMessage?, - val eventSink: (BlockedUsersEvents) -> Unit, + val eventSink: (BlockedUsersEvent) -> Unit, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewParam.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewParam.kt index b1a9a2224d..6fce0ba1f1 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewParam.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewParam.kt @@ -32,7 +32,7 @@ internal fun aBlockedUsersState( blockedUsers: List = aMatrixUserList(), unblockUserAction: AsyncAction = AsyncAction.Uninitialized, snackbarMessage: SnackbarMessage? = null, - eventSink: (BlockedUsersEvents) -> Unit = {}, + eventSink: (BlockedUsersEvent) -> Unit = {}, ): BlockedUsersState { return BlockedUsersState( blockedUsers = blockedUsers.toImmutableList(), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersView.kt index be0c9d4937..be6c0ef170 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersView.kt @@ -64,8 +64,8 @@ fun BlockedUsersView( items(state.blockedUsers) { matrixUser -> BlockedUserItem( matrixUser = matrixUser, - onClick = { state.eventSink(BlockedUsersEvents.Unblock(it)) }, - onLongClick = { state.eventSink(BlockedUsersEvents.CopyToClipboard(it)) }, + onClick = { state.eventSink(BlockedUsersEvent.Unblock(it)) }, + onLongClick = { state.eventSink(BlockedUsersEvent.CopyToClipboard(it)) }, ) } } @@ -99,8 +99,8 @@ fun BlockedUsersView( title = stringResource(R.string.screen_blocked_users_unblock_alert_title), content = stringResource(R.string.screen_blocked_users_unblock_alert_description), submitText = stringResource(R.string.screen_blocked_users_unblock_alert_action), - onSubmitClick = { state.eventSink(BlockedUsersEvents.ConfirmUnblock) }, - onDismiss = { state.eventSink(BlockedUsersEvents.Cancel) } + onSubmitClick = { state.eventSink(BlockedUsersEvent.ConfirmUnblock) }, + onDismiss = { state.eventSink(BlockedUsersEvent.Cancel) } ) } else -> Unit diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUserViewTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUserViewTest.kt index 58e0cf467f..99ae35e70f 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUserViewTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUserViewTest.kt @@ -33,7 +33,7 @@ import org.junit.Test class BlockedUserViewTest : RobolectricTest() { @Test fun `clicking on back invokes back callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> setBlockedUsersView( aBlockedUsersState( @@ -47,7 +47,7 @@ class BlockedUserViewTest : RobolectricTest() { @Test fun `clicking on a user emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val userList = aMatrixUserList() setBlockedUsersView( aBlockedUsersState( @@ -56,12 +56,12 @@ class BlockedUserViewTest : RobolectricTest() { ), ) onNodeWithText(userList.first().displayName.orEmpty()).performClick() - eventsRecorder.assertSingle(BlockedUsersEvents.Unblock(userList.first().userId)) + eventsRecorder.assertSingle(BlockedUsersEvent.Unblock(userList.first().userId)) } @Test fun `long clicking on a user emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val userList = aMatrixUserList() setBlockedUsersView( aBlockedUsersState( @@ -70,12 +70,12 @@ class BlockedUserViewTest : RobolectricTest() { ), ) onNodeWithText(userList.first().displayName.orEmpty()).performTouchInput { longClick() } - eventsRecorder.assertSingle(BlockedUsersEvents.CopyToClipboard(userList.first().userId)) + eventsRecorder.assertSingle(BlockedUsersEvent.CopyToClipboard(userList.first().userId)) } @Test - fun `clicking on cancel sends a BlockedUsersEvents`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + fun `clicking on cancel sends a BlockedUsersEvent`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() setBlockedUsersView( aBlockedUsersState( unblockUserAction = AsyncAction.ConfirmingNoParams, @@ -83,12 +83,12 @@ class BlockedUserViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_cancel) - eventsRecorder.assertSingle(BlockedUsersEvents.Cancel) + eventsRecorder.assertSingle(BlockedUsersEvent.Cancel) } @Test - fun `clicking on confirm sends a BlockedUsersEvents`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + fun `clicking on confirm sends a BlockedUsersEvent`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() setBlockedUsersView( aBlockedUsersState( unblockUserAction = AsyncAction.ConfirmingNoParams, @@ -96,7 +96,7 @@ class BlockedUserViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_blocked_users_unblock_alert_action) - eventsRecorder.assertSingle(BlockedUsersEvents.ConfirmUnblock) + eventsRecorder.assertSingle(BlockedUsersEvent.ConfirmUnblock) } } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenterTest.kt index 4e0164b2a0..828a0fc59a 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenterTest.kt @@ -118,10 +118,10 @@ class BlockedUsersPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(BlockedUsersEvents.Unblock(A_USER_ID)) + initialState.eventSink(BlockedUsersEvent.Unblock(A_USER_ID)) assertThat(awaitItem().unblockUserAction).isInstanceOf(AsyncAction.Confirming::class.java) - initialState.eventSink(BlockedUsersEvents.ConfirmUnblock) + initialState.eventSink(BlockedUsersEvent.ConfirmUnblock) assertThat(awaitItem().unblockUserAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().unblockUserAction).isInstanceOf(AsyncAction.Success::class.java) @@ -141,10 +141,10 @@ class BlockedUsersPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(BlockedUsersEvents.Unblock(A_USER_ID)) + initialState.eventSink(BlockedUsersEvent.Unblock(A_USER_ID)) assertThat(awaitItem().unblockUserAction).isInstanceOf(AsyncAction.Confirming::class.java) - initialState.eventSink(BlockedUsersEvents.ConfirmUnblock) + initialState.eventSink(BlockedUsersEvent.ConfirmUnblock) assertThat(awaitItem().unblockUserAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().unblockUserAction).isInstanceOf(AsyncAction.Failure::class.java) @@ -163,10 +163,10 @@ class BlockedUsersPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(BlockedUsersEvents.Unblock(A_USER_ID)) + initialState.eventSink(BlockedUsersEvent.Unblock(A_USER_ID)) assertThat(awaitItem().unblockUserAction).isInstanceOf(AsyncAction.Confirming::class.java) - initialState.eventSink(BlockedUsersEvents.Cancel) + initialState.eventSink(BlockedUsersEvent.Cancel) assertThat(awaitItem().unblockUserAction).isEqualTo(AsyncAction.Uninitialized) } @@ -187,7 +187,7 @@ class BlockedUsersPresenterTest { }.test { val initialState = awaitItem() assertThat(initialState.snackbarMessage).isNull() - initialState.eventSink(BlockedUsersEvents.CopyToClipboard(A_USER_ID)) + initialState.eventSink(BlockedUsersEvent.CopyToClipboard(A_USER_ID)) assertThat(clipboardHelper.clipboardContents).isEqualTo(A_USER_ID.value) assertThat(awaitItem().snackbarMessage?.messageResId).isEqualTo(CommonStrings.common_copied_to_clipboard) @@ -201,7 +201,7 @@ class BlockedUsersPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().eventSink(BlockedUsersEvents.ConfirmUnblock) + awaitItem().eventSink(BlockedUsersEvent.ConfirmUnblock) ensureAllEventsConsumed() } } From 7952721ffee2792ff0ffaccb89449fd9506de3f9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:09:09 +0200 Subject: [PATCH 041/112] `DeveloperSettingsEvents` -> `DeveloperSettingsEvent` --- ...ingsEvents.kt => DeveloperSettingsEvent.kt} | 14 +++++++------- .../developer/DeveloperSettingsPresenter.kt | 14 +++++++------- .../impl/developer/DeveloperSettingsState.kt | 2 +- .../DeveloperSettingsStatePreviewParam.kt | 2 +- .../impl/developer/DeveloperSettingsView.kt | 18 +++++++++--------- .../DeveloperSettingsPresenterTest.kt | 16 ++++++++-------- .../developer/DeveloperSettingsViewTest.kt | 10 +++++----- 7 files changed, 38 insertions(+), 38 deletions(-) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/{DeveloperSettingsEvents.kt => DeveloperSettingsEvent.kt} (73%) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvent.kt similarity index 73% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvents.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvent.kt index 2d6c2b0904..e0ad208fc1 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvent.kt @@ -10,11 +10,11 @@ package io.element.android.features.preferences.impl.developer import androidx.compose.ui.graphics.Color -sealed interface DeveloperSettingsEvents { - data class SetShowColorPicker(val show: Boolean) : DeveloperSettingsEvents - data class ChangeBrandColor(val color: Color?) : DeveloperSettingsEvents - data object ClearCache : DeveloperSettingsEvents - data object VacuumStores : DeveloperSettingsEvents - data class MarkAllRoomsAsRead(val needsConfirmation: Boolean) : DeveloperSettingsEvents - data object DismissMarkAllRoomsAsReadConfirmation : DeveloperSettingsEvents +sealed interface DeveloperSettingsEvent { + data class SetShowColorPicker(val show: Boolean) : DeveloperSettingsEvent + data class ChangeBrandColor(val color: Color?) : DeveloperSettingsEvent + data object ClearCache : DeveloperSettingsEvent + data object VacuumStores : DeveloperSettingsEvent + data class MarkAllRoomsAsRead(val needsConfirmation: Boolean) : DeveloperSettingsEvent + data object DismissMarkAllRoomsAsReadConfirmation : DeveloperSettingsEvent } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt index c5a9b430ee..c925433608 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt @@ -79,10 +79,10 @@ class DeveloperSettingsPresenter( computeCacheSize(cacheSize) } - fun handleEvent(event: DeveloperSettingsEvents) { + fun handleEvent(event: DeveloperSettingsEvent) { when (event) { - DeveloperSettingsEvents.ClearCache -> coroutineScope.clearCache(clearCacheAction) - is DeveloperSettingsEvents.ChangeBrandColor -> coroutineScope.launch { + DeveloperSettingsEvent.ClearCache -> coroutineScope.clearCache(clearCacheAction) + is DeveloperSettingsEvent.ChangeBrandColor -> coroutineScope.launch { showColorPicker = false val color = event.color ?.toArgb() @@ -91,13 +91,13 @@ class DeveloperSettingsPresenter( ?.padStart(7, '#') enterpriseService.overrideBrandColor(sessionId, color) } - is DeveloperSettingsEvents.SetShowColorPicker -> { + is DeveloperSettingsEvent.SetShowColorPicker -> { showColorPicker = event.show } - DeveloperSettingsEvents.VacuumStores -> coroutineScope.launch { + DeveloperSettingsEvent.VacuumStores -> coroutineScope.launch { vacuumStoresUseCase() } - is DeveloperSettingsEvents.MarkAllRoomsAsRead -> { + is DeveloperSettingsEvent.MarkAllRoomsAsRead -> { if (event.needsConfirmation) { markAllRoomsAsReadAction.value = AsyncAction.ConfirmingNoParams } else { @@ -106,7 +106,7 @@ class DeveloperSettingsPresenter( ) } } - DeveloperSettingsEvents.DismissMarkAllRoomsAsReadConfirmation -> { + DeveloperSettingsEvent.DismissMarkAllRoomsAsReadConfirmation -> { markAllRoomsAsReadAction.value = AsyncAction.Uninitialized } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsState.kt index 700c989bfa..3a859f14bb 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsState.kt @@ -23,7 +23,7 @@ data class DeveloperSettingsState( val isEnterpriseBuild: Boolean, val showColorPicker: Boolean, val deviceId: DeviceId, - val eventSink: (DeveloperSettingsEvents) -> Unit + val eventSink: (DeveloperSettingsEvent) -> Unit ) { val showLoader = clearCacheAction is AsyncAction.Loading || markAllRoomsAsReadAction is AsyncAction.Loading } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStatePreviewParam.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStatePreviewParam.kt index 8cc6ad7dce..cbe95d6b20 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStatePreviewParam.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStatePreviewParam.kt @@ -42,7 +42,7 @@ fun aDeveloperSettingsState( isEnterpriseBuild: Boolean = false, showColorPicker: Boolean = false, deviceId: DeviceId = DeviceId("ILAKNDNASDLK"), - eventSink: (DeveloperSettingsEvents) -> Unit = {}, + eventSink: (DeveloperSettingsEvent) -> Unit = {}, ) = DeveloperSettingsState( appDeveloperSettingsState = appDeveloperSettingsState, cacheSize = AsyncData.Success("1.2 MB"), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt index 66a478a4e5..e143f42b0b 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt @@ -56,8 +56,8 @@ fun DeveloperSettingsView( title = "Are you sure you want to mark all the rooms as read?", content = "", submitText = stringResource(CommonStrings.action_yes), - onSubmitClick = { state.eventSink(DeveloperSettingsEvents.MarkAllRoomsAsRead(needsConfirmation = false)) }, - onDismiss = { state.eventSink(DeveloperSettingsEvents.DismissMarkAllRoomsAsReadConfirmation) }, + onSubmitClick = { state.eventSink(DeveloperSettingsEvent.MarkAllRoomsAsRead(needsConfirmation = false)) }, + onDismiss = { state.eventSink(DeveloperSettingsEvent.DismissMarkAllRoomsAsReadConfirmation) }, ) } BackHandler( @@ -89,7 +89,7 @@ fun DeveloperSettingsView( Text("Change brand color") }, onClick = { - state.eventSink(DeveloperSettingsEvents.SetShowColorPicker(true)) + state.eventSink(DeveloperSettingsEvent.SetShowColorPicker(true)) } ) ListItem( @@ -97,7 +97,7 @@ fun DeveloperSettingsView( Text("Reset brand color") }, onClick = { - state.eventSink(DeveloperSettingsEvents.ChangeBrandColor(null)) + state.eventSink(DeveloperSettingsEvent.ChangeBrandColor(null)) } ) } @@ -131,7 +131,7 @@ fun DeveloperSettingsView( Text("Vacuum stores") }, onClick = { - state.eventSink(DeveloperSettingsEvents.VacuumStores) + state.eventSink(DeveloperSettingsEvent.VacuumStores) } ) ListItem( @@ -152,7 +152,7 @@ fun DeveloperSettingsView( }, onClick = { if (state.clearCacheAction.isLoading().not()) { - state.eventSink(DeveloperSettingsEvents.ClearCache) + state.eventSink(DeveloperSettingsEvent.ClearCache) } } ) @@ -164,10 +164,10 @@ fun DeveloperSettingsView( showAlphaBar = false, ), onDismissRequest = { - state.eventSink(DeveloperSettingsEvents.SetShowColorPicker(false)) + state.eventSink(DeveloperSettingsEvent.SetShowColorPicker(false)) }, onPickedColor = { - state.eventSink(DeveloperSettingsEvents.ChangeBrandColor(it)) + state.eventSink(DeveloperSettingsEvent.ChangeBrandColor(it)) }, ) } @@ -210,7 +210,7 @@ private fun MarkAllRoomsAsReadCategory(state: DeveloperSettingsState) { }, enabled = !state.showLoader, onClick = { - state.eventSink(DeveloperSettingsEvents.MarkAllRoomsAsRead(needsConfirmation = true)) + state.eventSink(DeveloperSettingsEvent.MarkAllRoomsAsRead(needsConfirmation = true)) }, ) } 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 3b2645783f..753d89da95 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 @@ -88,7 +88,7 @@ class DeveloperSettingsPresenterTest { skipItems(2) assertThat(clearCacheUseCase.executeHasBeenCalled).isFalse() awaitItem().also { state -> - state.eventSink(DeveloperSettingsEvents.ClearCache) + state.eventSink(DeveloperSettingsEvent.ClearCache) } awaitItem().also { state -> assertThat(state.clearCacheAction).isInstanceOf(AsyncAction.Loading::class.java) @@ -117,13 +117,13 @@ class DeveloperSettingsPresenterTest { skipItems(1) val initialState = awaitItem() assertThat(initialState.isEnterpriseBuild).isTrue() - initialState.eventSink(DeveloperSettingsEvents.SetShowColorPicker(true)) + initialState.eventSink(DeveloperSettingsEvent.SetShowColorPicker(true)) assertThat(awaitItem().showColorPicker).isTrue() - initialState.eventSink(DeveloperSettingsEvents.SetShowColorPicker(false)) + initialState.eventSink(DeveloperSettingsEvent.SetShowColorPicker(false)) assertThat(awaitItem().showColorPicker).isFalse() - initialState.eventSink(DeveloperSettingsEvents.SetShowColorPicker(true)) + initialState.eventSink(DeveloperSettingsEvent.SetShowColorPicker(true)) assertThat(awaitItem().showColorPicker).isTrue() - initialState.eventSink(DeveloperSettingsEvents.ChangeBrandColor(Color.Green)) + initialState.eventSink(DeveloperSettingsEvent.ChangeBrandColor(Color.Green)) assertThat(awaitItem().showColorPicker).isFalse() skipItems(1) overrideBrandColorResult.assertions().isCalledOnce() @@ -138,10 +138,10 @@ class DeveloperSettingsPresenterTest { presenter.test { skipItems(2) val initialState = awaitItem() - initialState.eventSink(DeveloperSettingsEvents.MarkAllRoomsAsRead(needsConfirmation = true)) + initialState.eventSink(DeveloperSettingsEvent.MarkAllRoomsAsRead(needsConfirmation = true)) val stateWithConfirmation = awaitItem() assertThat(stateWithConfirmation.markAllRoomsAsReadAction.isConfirming()).isTrue() - stateWithConfirmation.eventSink(DeveloperSettingsEvents.MarkAllRoomsAsRead(needsConfirmation = false)) + stateWithConfirmation.eventSink(DeveloperSettingsEvent.MarkAllRoomsAsRead(needsConfirmation = false)) awaitItem().also { state -> assertThat(state.markAllRoomsAsReadAction.isConfirming()).isFalse() assertThat(state.markAllRoomsAsReadAction).isInstanceOf(AsyncAction.Loading::class.java) @@ -164,7 +164,7 @@ class DeveloperSettingsPresenterTest { presenter.test { val state = awaitItem() assertThat(vacuumCalled).isFalse() - state.eventSink(DeveloperSettingsEvents.VacuumStores) + state.eventSink(DeveloperSettingsEvent.VacuumStores) skipItems(1) assertThat(vacuumCalled).isTrue() } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsViewTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsViewTest.kt index a16ad77d48..f1151bd3e1 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsViewTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsViewTest.kt @@ -30,7 +30,7 @@ import org.robolectric.annotation.Config class DeveloperSettingsViewTest : RobolectricTest() { @Test fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setDeveloperSettingsView( state = aDeveloperSettingsState( @@ -45,7 +45,7 @@ class DeveloperSettingsViewTest : RobolectricTest() { @Config(qualifiers = "h2400dp") @Test fun `clicking on push history notification invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setDeveloperSettingsView( state = aDeveloperSettingsState( @@ -60,7 +60,7 @@ class DeveloperSettingsViewTest : RobolectricTest() { @Config(qualifiers = "h2000dp") @Test fun `clicking on open showkase invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setDeveloperSettingsView( state = aDeveloperSettingsState( @@ -75,14 +75,14 @@ class DeveloperSettingsViewTest : RobolectricTest() { @Config(qualifiers = "h2400dp") @Test fun `clicking on clear cache emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setDeveloperSettingsView( state = aDeveloperSettingsState( eventSink = eventsRecorder ), ) onNodeWithText("Clear cache").performScrollTo().performClick() - eventsRecorder.assertSingle(DeveloperSettingsEvents.ClearCache) + eventsRecorder.assertSingle(DeveloperSettingsEvent.ClearCache) } } From a113d445588c0bf520e58a496a8db9ebb0dfa394 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:10:11 +0200 Subject: [PATCH 042/112] `LabsEvents` -> `LabsEvent` --- .../preferences/impl/labs/{LabsEvents.kt => LabsEvent.kt} | 4 ++-- .../android/features/preferences/impl/labs/LabsPresenter.kt | 4 ++-- .../android/features/preferences/impl/labs/LabsState.kt | 2 +- .../android/features/preferences/impl/labs/LabsView.kt | 2 +- .../features/preferences/impl/labs/LabsPresenterTest.kt | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/{LabsEvents.kt => LabsEvent.kt} (92%) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsEvent.kt similarity index 92% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsEvents.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsEvent.kt index bc948da09c..e2c60327eb 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsEvent.kt @@ -10,6 +10,6 @@ package io.element.android.features.preferences.impl.labs import io.element.android.libraries.featureflag.ui.model.FeatureUiModel -sealed interface LabsEvents { - data class ToggleFeature(val feature: FeatureUiModel) : LabsEvents +sealed interface LabsEvent { + data class ToggleFeature(val feature: FeatureUiModel) : LabsEvent } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsPresenter.kt index 3b7d499ecf..f9576f93e4 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsPresenter.kt @@ -54,9 +54,9 @@ class LabsPresenter( var isApplyingChanges by remember { mutableStateOf(false) } val featureUiModels = createUiModels(enabledFeatures) - fun handleEvent(event: LabsEvents) { + fun handleEvent(event: LabsEvent) { when (event) { - is LabsEvents.ToggleFeature -> coroutineScope.launch { + is LabsEvent.ToggleFeature -> coroutineScope.launch { val featureIndex = enabledFeatures.indexOfFirst { it.feature.key == event.feature.key }.takeIf { it != -1 } ?: return@launch val enabledFeature = enabledFeatures[featureIndex] val feature = enabledFeature.feature diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsState.kt index 42e70d58c9..55c20d1b43 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsState.kt @@ -14,5 +14,5 @@ import kotlinx.collections.immutable.ImmutableList data class LabsState( val features: ImmutableList, val isApplyingChanges: Boolean, - val eventSink: (LabsEvents) -> Unit, + val eventSink: (LabsEvent) -> Unit, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsView.kt index 7c7f2482e3..c518b346eb 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsView.kt @@ -90,7 +90,7 @@ fun LabsView( supportingText = feature.description, value = feature.isEnabled, onChange = { - state.eventSink(LabsEvents.ToggleFeature(feature)) + state.eventSink(LabsEvent.ToggleFeature(feature)) } ) } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/labs/LabsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/labs/LabsPresenterTest.kt index acf65ef2e6..1173ce9c5f 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/labs/LabsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/labs/LabsPresenterTest.kt @@ -70,10 +70,10 @@ class LabsPresenterTest { val feature = initialItem.features.first() assertThat(feature.isEnabled).isFalse() // Toggle the feature, should be true now - initialItem.eventSink(LabsEvents.ToggleFeature(feature)) + initialItem.eventSink(LabsEvent.ToggleFeature(feature)) assertThat(awaitItem().features.first().isEnabled).isTrue() // Toggle the feature, should be false now - initialItem.eventSink(LabsEvents.ToggleFeature(feature)) + initialItem.eventSink(LabsEvent.ToggleFeature(feature)) assertThat(awaitItem().features.first().isEnabled).isFalse() } } @@ -99,7 +99,7 @@ class LabsPresenterTest { assertThat(feature.isEnabled).isFalse() assertThat(initialItem.isApplyingChanges).isFalse() // Toggle the feature - initialItem.eventSink(LabsEvents.ToggleFeature(feature)) + initialItem.eventSink(LabsEvent.ToggleFeature(feature)) assertThat(awaitItem().features.first().isEnabled).isTrue() // The clear cache use case should have been called assertThat(awaitItem().isApplyingChanges).isTrue() From 8a02b50c98b961fddd94d3d94422e8fc22f720b7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:11:07 +0200 Subject: [PATCH 043/112] `NotificationSettingsEvents` -> `NotificationSettingsEvent` --- ...Events.kt => NotificationSettingsEvent.kt} | 48 ++++----- .../NotificationSettingsPresenter.kt | 48 ++++----- .../NotificationSettingsState.kt | 4 +- .../NotificationSettingsStatePreviewParam.kt | 4 +- .../notifications/NotificationSettingsView.kt | 50 ++++----- .../NotificationSettingsPresenterTest.kt | 102 +++++++++--------- .../NotificationSettingsViewTest.kt | 72 ++++++------- 7 files changed, 164 insertions(+), 164 deletions(-) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/{NotificationSettingsEvents.kt => NotificationSettingsEvent.kt} (79%) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvent.kt similarity index 79% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvent.kt index 5ca7fc9fdf..fc175f4c0b 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvent.kt @@ -10,28 +10,28 @@ package io.element.android.features.preferences.impl.notifications import io.element.android.libraries.preferences.api.store.NotificationSound -sealed interface NotificationSettingsEvents { - data object RefreshSystemNotificationsEnabled : NotificationSettingsEvents - data class SetNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents - data class SetAtRoomNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents - data class SetCallNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents - data class SetInviteForMeNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents - data object FixConfigurationMismatch : NotificationSettingsEvents - data object ClearConfigurationMismatchError : NotificationSettingsEvents - data object ClearNotificationChangeError : NotificationSettingsEvents - data object ChangePushProvider : NotificationSettingsEvents - data object CancelChangePushProvider : NotificationSettingsEvents - data class SetPushProvider(val index: Int) : NotificationSettingsEvents - data class SetMessageSound(val sound: NotificationSound) : NotificationSettingsEvents - data class SetCallRingtone(val sound: NotificationSound) : NotificationSettingsEvents - data object DismissMessageSoundCopyError : NotificationSettingsEvents - data object DismissCallRingtoneCopyError : NotificationSettingsEvents - data object ShowMessageSoundDialog : NotificationSettingsEvents - data object DismissMessageSoundDialog : NotificationSettingsEvents - data class SelectMessageSoundPreset(val sound: NotificationSound) : NotificationSettingsEvents - data object LaunchMessageSoundPicker : NotificationSettingsEvents - data object ShowCallRingtoneDialog : NotificationSettingsEvents - data object DismissCallRingtoneDialog : NotificationSettingsEvents - data class SelectCallRingtonePreset(val sound: NotificationSound) : NotificationSettingsEvents - data object LaunchCallRingtonePicker : NotificationSettingsEvents +sealed interface NotificationSettingsEvent { + data object RefreshSystemNotificationsEnabled : NotificationSettingsEvent + data class SetNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvent + data class SetAtRoomNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvent + data class SetCallNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvent + data class SetInviteForMeNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvent + data object FixConfigurationMismatch : NotificationSettingsEvent + data object ClearConfigurationMismatchError : NotificationSettingsEvent + data object ClearNotificationChangeError : NotificationSettingsEvent + data object ChangePushProvider : NotificationSettingsEvent + data object CancelChangePushProvider : NotificationSettingsEvent + data class SetPushProvider(val index: Int) : NotificationSettingsEvent + data class SetMessageSound(val sound: NotificationSound) : NotificationSettingsEvent + data class SetCallRingtone(val sound: NotificationSound) : NotificationSettingsEvent + data object DismissMessageSoundCopyError : NotificationSettingsEvent + data object DismissCallRingtoneCopyError : NotificationSettingsEvent + data object ShowMessageSoundDialog : NotificationSettingsEvent + data object DismissMessageSoundDialog : NotificationSettingsEvent + data class SelectMessageSoundPreset(val sound: NotificationSound) : NotificationSettingsEvent + data object LaunchMessageSoundPicker : NotificationSettingsEvent + data object ShowCallRingtoneDialog : NotificationSettingsEvent + data object DismissCallRingtoneDialog : NotificationSettingsEvent + data class SelectCallRingtonePreset(val sound: NotificationSound) : NotificationSettingsEvent + data object LaunchCallRingtonePicker : NotificationSettingsEvent } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt index 03fe2cf31b..5256f9ec9c 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt @@ -196,38 +196,38 @@ class NotificationSettingsPresenter( ) } - fun handleEvent(event: NotificationSettingsEvents) { + fun handleEvent(event: NotificationSettingsEvent) { when (event) { - is NotificationSettingsEvents.SetAtRoomNotificationsEnabled -> { + is NotificationSettingsEvent.SetAtRoomNotificationsEnabled -> { localCoroutineScope.setAtRoomNotificationsEnabled(event.enabled, changeNotificationSettingAction) } - is NotificationSettingsEvents.SetCallNotificationsEnabled -> { + is NotificationSettingsEvent.SetCallNotificationsEnabled -> { localCoroutineScope.setCallNotificationsEnabled(event.enabled, changeNotificationSettingAction) } - is NotificationSettingsEvents.SetInviteForMeNotificationsEnabled -> { + is NotificationSettingsEvent.SetInviteForMeNotificationsEnabled -> { localCoroutineScope.setInviteForMeNotificationsEnabled(event.enabled, changeNotificationSettingAction) } - is NotificationSettingsEvents.SetNotificationsEnabled -> sessionCoroutineScope.setNotificationsEnabled(userPushStore, event.enabled) - NotificationSettingsEvents.ClearConfigurationMismatchError -> { + is NotificationSettingsEvent.SetNotificationsEnabled -> sessionCoroutineScope.setNotificationsEnabled(userPushStore, event.enabled) + NotificationSettingsEvent.ClearConfigurationMismatchError -> { matrixSettings.value = NotificationSettingsState.MatrixSettings.Invalid(fixFailed = false) } - NotificationSettingsEvents.FixConfigurationMismatch -> localCoroutineScope.fixConfigurationMismatch(matrixSettings) - NotificationSettingsEvents.RefreshSystemNotificationsEnabled -> { + NotificationSettingsEvent.FixConfigurationMismatch -> localCoroutineScope.fixConfigurationMismatch(matrixSettings) + NotificationSettingsEvent.RefreshSystemNotificationsEnabled -> { systemNotificationsEnabled.value = systemNotificationsEnabledProvider.notificationsEnabled() refreshFullScreenIntentSettings++ } - NotificationSettingsEvents.ClearNotificationChangeError -> changeNotificationSettingAction.value = AsyncAction.Uninitialized - NotificationSettingsEvents.ChangePushProvider -> showChangePushProviderDialog = true - NotificationSettingsEvents.CancelChangePushProvider -> showChangePushProviderDialog = false - is NotificationSettingsEvents.SetPushProvider -> localCoroutineScope.changePushProvider(distributors.getOrNull(event.index)) - is NotificationSettingsEvents.SetMessageSound -> applyMessageSound( + NotificationSettingsEvent.ClearNotificationChangeError -> changeNotificationSettingAction.value = AsyncAction.Uninitialized + NotificationSettingsEvent.ChangePushProvider -> showChangePushProviderDialog = true + NotificationSettingsEvent.CancelChangePushProvider -> showChangePushProviderDialog = false + is NotificationSettingsEvent.SetPushProvider -> localCoroutineScope.changePushProvider(distributors.getOrNull(event.index)) + is NotificationSettingsEvent.SetMessageSound -> applyMessageSound( sound = event.sound, sessionCoroutineScope = sessionCoroutineScope, onCopyError = { messageSoundCopyError = true }, onCopySuccess = { messageSoundCopyError = false }, onChannelFailure = { failure -> changeNotificationSettingAction.value = AsyncAction.Failure(failure) }, ) - is NotificationSettingsEvents.SelectMessageSoundPreset -> { + is NotificationSettingsEvent.SelectMessageSoundPreset -> { showMessageSoundDialog = false applyMessageSound( sound = event.sound, @@ -237,13 +237,13 @@ class NotificationSettingsPresenter( onChannelFailure = { failure -> changeNotificationSettingAction.value = AsyncAction.Failure(failure) }, ) } - NotificationSettingsEvents.ShowMessageSoundDialog -> showMessageSoundDialog = true - NotificationSettingsEvents.DismissMessageSoundDialog -> showMessageSoundDialog = false - NotificationSettingsEvents.LaunchMessageSoundPicker -> { + NotificationSettingsEvent.ShowMessageSoundDialog -> showMessageSoundDialog = true + NotificationSettingsEvent.DismissMessageSoundDialog -> showMessageSoundDialog = false + NotificationSettingsEvent.LaunchMessageSoundPicker -> { showMessageSoundDialog = false pendingMessageSoundPickerLaunch++ } - is NotificationSettingsEvents.SetCallRingtone -> { + is NotificationSettingsEvent.SetCallRingtone -> { legacyCallRingtone = null applyCallRingtone( sound = event.sound, @@ -253,7 +253,7 @@ class NotificationSettingsPresenter( onChannelFailure = { failure -> changeNotificationSettingAction.value = AsyncAction.Failure(failure) }, ) } - is NotificationSettingsEvents.SelectCallRingtonePreset -> { + is NotificationSettingsEvent.SelectCallRingtonePreset -> { showCallRingtoneDialog = false legacyCallRingtone = null applyCallRingtone( @@ -264,16 +264,16 @@ class NotificationSettingsPresenter( onChannelFailure = { failure -> changeNotificationSettingAction.value = AsyncAction.Failure(failure) }, ) } - NotificationSettingsEvents.ShowCallRingtoneDialog -> showCallRingtoneDialog = true - NotificationSettingsEvents.DismissCallRingtoneDialog -> showCallRingtoneDialog = false - NotificationSettingsEvents.LaunchCallRingtonePicker -> { + NotificationSettingsEvent.ShowCallRingtoneDialog -> showCallRingtoneDialog = true + NotificationSettingsEvent.DismissCallRingtoneDialog -> showCallRingtoneDialog = false + NotificationSettingsEvent.LaunchCallRingtonePicker -> { showCallRingtoneDialog = false pendingCallRingtonePickerLaunch++ } - NotificationSettingsEvents.DismissMessageSoundCopyError -> { + NotificationSettingsEvent.DismissMessageSoundCopyError -> { messageSoundCopyError = false } - NotificationSettingsEvents.DismissCallRingtoneCopyError -> { + NotificationSettingsEvent.DismissCallRingtoneCopyError -> { callRingtoneCopyError = false } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt index 3c59f6c355..379b8589b7 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt @@ -30,7 +30,7 @@ data class NotificationSettingsState( val showCallRingtoneDialog: Boolean, /** * One-shot trigger for launching the system ringtone picker. Each - * [NotificationSettingsEvents.LaunchMessageSoundPicker] increments the value; + * [NotificationSettingsEvent.LaunchMessageSoundPicker] increments the value; * the view watches this in `LaunchedEffect` and calls the launcher whenever it goes above 0. * **Always start at 0** in initial state, providers, and previews — a non-zero seed will * auto-open the picker on screen entry. @@ -42,7 +42,7 @@ data class NotificationSettingsState( * auto-opens on screen entry. */ val pendingCallRingtonePickerLaunch: Int, - val eventSink: (NotificationSettingsEvents) -> Unit, + val eventSink: (NotificationSettingsEvent) -> Unit, ) { sealed interface MatrixSettings { data object Uninitialized : MatrixSettings diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStatePreviewParam.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStatePreviewParam.kt index 38c83153ea..33f20cdb6e 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStatePreviewParam.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStatePreviewParam.kt @@ -101,7 +101,7 @@ fun aValidNotificationSettingsState( showCallRingtoneDialog: Boolean = false, pendingMessageSoundPickerLaunch: Int = 0, pendingCallRingtonePickerLaunch: Int = 0, - eventSink: (NotificationSettingsEvents) -> Unit = {}, + eventSink: (NotificationSettingsEvent) -> Unit = {}, ) = NotificationSettingsState( matrixSettings = NotificationSettingsState.MatrixSettings.Valid( atRoomNotificationsEnabled = atRoomNotificationsEnabled, @@ -138,7 +138,7 @@ fun aValidNotificationSettingsState( fun aInvalidNotificationSettingsState( fixFailed: Boolean = false, - eventSink: (NotificationSettingsEvents) -> Unit = {}, + eventSink: (NotificationSettingsEvent) -> Unit = {}, ) = NotificationSettingsState( matrixSettings = NotificationSettingsState.MatrixSettings.Invalid( fixFailed = fixFailed, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt index a15f991f95..6c5b053b35 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt @@ -74,7 +74,7 @@ fun NotificationSettingsView( ) { OnLifecycleEvent { _, event -> when (event) { - Lifecycle.Event.ON_RESUME -> state.eventSink.invoke(NotificationSettingsEvents.RefreshSystemNotificationsEnabled) + Lifecycle.Event.ON_RESUME -> state.eventSink.invoke(NotificationSettingsEvent.RefreshSystemNotificationsEnabled) else -> Unit } } @@ -86,27 +86,27 @@ fun NotificationSettingsView( when (state.matrixSettings) { is NotificationSettingsState.MatrixSettings.Invalid -> InvalidNotificationSettingsView( showError = state.matrixSettings.fixFailed, - onContinueClick = { state.eventSink(NotificationSettingsEvents.FixConfigurationMismatch) }, - onDismissError = { state.eventSink(NotificationSettingsEvents.ClearConfigurationMismatchError) }, + onContinueClick = { state.eventSink(NotificationSettingsEvent.FixConfigurationMismatch) }, + onDismissError = { state.eventSink(NotificationSettingsEvent.ClearConfigurationMismatchError) }, ) NotificationSettingsState.MatrixSettings.Uninitialized -> return@PreferencePage is NotificationSettingsState.MatrixSettings.Valid -> NotificationSettingsContentView( matrixSettings = state.matrixSettings, state = state, - onNotificationsEnabledChange = { state.eventSink(NotificationSettingsEvents.SetNotificationsEnabled(it)) }, + onNotificationsEnabledChange = { state.eventSink(NotificationSettingsEvent.SetNotificationsEnabled(it)) }, onGroupChatsClick = { onOpenEditDefault(false) }, onDirectChatsClick = { onOpenEditDefault(true) }, - onMentionNotificationsChange = { state.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(it)) }, + onMentionNotificationsChange = { state.eventSink(NotificationSettingsEvent.SetAtRoomNotificationsEnabled(it)) }, // TODO We are removing the call notification toggle until support for call notifications has been added -// onCallsNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(it)) }, - onInviteForMeNotificationsChange = { state.eventSink(NotificationSettingsEvents.SetInviteForMeNotificationsEnabled(it)) }, +// onCallsNotificationsChanged = { state.eventSink(NotificationSettingsEvent.SetCallNotificationsEnabled(it)) }, + onInviteForMeNotificationsChange = { state.eventSink(NotificationSettingsEvent.SetInviteForMeNotificationsEnabled(it)) }, onTroubleshootNotificationsClick = onTroubleshootNotificationsClick, ) } AsyncActionView( async = state.changeNotificationSettingAction, errorMessage = { stringResource(R.string.screen_notification_settings_edit_failed_updating_default_mode) }, - onErrorDismiss = { state.eventSink(NotificationSettingsEvents.ClearNotificationChangeError) }, + onErrorDismiss = { state.eventSink(NotificationSettingsEvent.ClearNotificationChangeError) }, onSuccess = {}, ) } @@ -249,7 +249,7 @@ private fun NotificationSettingsContentView( }, onClick = { if (state.currentPushDistributor.isReady()) { - state.eventSink(NotificationSettingsEvents.ChangePushProvider) + state.eventSink(NotificationSettingsEvent.ChangePushProvider) } } ) @@ -269,10 +269,10 @@ private fun NotificationSettingsContentView( initialSelection = state.availablePushDistributors.indexOf(state.currentPushDistributor.dataOrNull()), onSelectOption = { index -> state.eventSink( - NotificationSettingsEvents.SetPushProvider(index) + NotificationSettingsEvent.SetPushProvider(index) ) }, - onDismissRequest = { state.eventSink(NotificationSettingsEvents.CancelChangePushProvider) }, + onDismissRequest = { state.eventSink(NotificationSettingsEvent.CancelChangePushProvider) }, ) } } @@ -286,7 +286,7 @@ private fun SoundsPreferenceCategory(state: NotificationSettingsState) { type = RingtoneManager.TYPE_NOTIFICATION, current = state.messageSound.sound, defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI, - onSoundPick = { sound -> state.eventSink(NotificationSettingsEvents.SetMessageSound(sound)) }, + onSoundPick = { sound -> state.eventSink(NotificationSettingsEvent.SetMessageSound(sound)) }, ) // Skip the initial 0 emission so the picker doesn't auto-open on screen entry; only // increments fired by LaunchMessageSoundPicker should launch it. @@ -298,11 +298,11 @@ private fun SoundsPreferenceCategory(state: NotificationSettingsState) { ListItem( content = { Text(stringResource(id = R.string.screen_notification_settings_message_sound_label)) }, supportingContent = { Text(state.messageSound.displayName) }, - onClick = { state.eventSink(NotificationSettingsEvents.ShowMessageSoundDialog) }, + onClick = { state.eventSink(NotificationSettingsEvent.ShowMessageSoundDialog) }, ) if (state.messageSound.copyError) { SoundCopyErrorRow( - onDismissClick = { state.eventSink(NotificationSettingsEvents.DismissMessageSoundCopyError) }, + onDismissClick = { state.eventSink(NotificationSettingsEvent.DismissMessageSoundCopyError) }, ) } if (state.showMessageSoundDialog) { @@ -313,7 +313,7 @@ private fun SoundsPreferenceCategory(state: NotificationSettingsState) { type = RingtoneManager.TYPE_RINGTONE, current = state.callRingtone.sound, defaultUri = Settings.System.DEFAULT_RINGTONE_URI, - onSoundPick = { sound -> state.eventSink(NotificationSettingsEvents.SetCallRingtone(sound)) }, + onSoundPick = { sound -> state.eventSink(NotificationSettingsEvent.SetCallRingtone(sound)) }, ) // Skip the initial 0 emission so the picker doesn't auto-open on screen entry; only // increments fired by LaunchCallRingtonePicker should launch it. @@ -325,11 +325,11 @@ private fun SoundsPreferenceCategory(state: NotificationSettingsState) { ListItem( content = { Text(stringResource(id = R.string.screen_notification_settings_call_ringtone_label)) }, supportingContent = { Text(state.callRingtone.displayName) }, - onClick = { state.eventSink(NotificationSettingsEvents.ShowCallRingtoneDialog) }, + onClick = { state.eventSink(NotificationSettingsEvent.ShowCallRingtoneDialog) }, ) if (state.callRingtone.copyError) { SoundCopyErrorRow( - onDismissClick = { state.eventSink(NotificationSettingsEvents.DismissCallRingtoneCopyError) }, + onDismissClick = { state.eventSink(NotificationSettingsEvent.DismissCallRingtoneCopyError) }, ) } if (state.showCallRingtoneDialog) { @@ -368,13 +368,13 @@ private fun MessageSoundDialog(state: NotificationSettingsState) { initialSelection = initialSelection, onSelectOption = { index -> when (index) { - 0 -> state.eventSink(NotificationSettingsEvents.SelectMessageSoundPreset(NotificationSound.ElementDefault)) - 1 -> state.eventSink(NotificationSettingsEvents.SelectMessageSoundPreset(NotificationSound.ElementFade)) - 2 -> state.eventSink(NotificationSettingsEvents.SelectMessageSoundPreset(NotificationSound.SystemDefault)) - else -> state.eventSink(NotificationSettingsEvents.LaunchMessageSoundPicker) + 0 -> state.eventSink(NotificationSettingsEvent.SelectMessageSoundPreset(NotificationSound.ElementDefault)) + 1 -> state.eventSink(NotificationSettingsEvent.SelectMessageSoundPreset(NotificationSound.ElementFade)) + 2 -> state.eventSink(NotificationSettingsEvent.SelectMessageSoundPreset(NotificationSound.SystemDefault)) + else -> state.eventSink(NotificationSettingsEvent.LaunchMessageSoundPicker) } }, - onDismissRequest = { state.eventSink(NotificationSettingsEvents.DismissMessageSoundDialog) }, + onDismissRequest = { state.eventSink(NotificationSettingsEvent.DismissMessageSoundDialog) }, ) } @@ -402,11 +402,11 @@ private fun CallRingtoneDialog(state: NotificationSettingsState) { initialSelection = initialSelection, onSelectOption = { index -> when (index) { - 0 -> state.eventSink(NotificationSettingsEvents.SelectCallRingtonePreset(NotificationSound.SystemDefault)) - else -> state.eventSink(NotificationSettingsEvents.LaunchCallRingtonePicker) + 0 -> state.eventSink(NotificationSettingsEvent.SelectCallRingtonePreset(NotificationSound.SystemDefault)) + else -> state.eventSink(NotificationSettingsEvent.LaunchCallRingtonePicker) } }, - onDismissRequest = { state.eventSink(NotificationSettingsEvents.DismissCallRingtoneDialog) }, + onDismissRequest = { state.eventSink(NotificationSettingsEvent.DismissCallRingtoneDialog) }, ) } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt index 29e4b1c40a..6a201fe3fa 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt @@ -120,7 +120,7 @@ class NotificationSettingsPresenterTest { val presenter = createNotificationSettingsPresenter(notificationSettingsService) presenter.test { val initialState = awaitItem() - initialState.eventSink(NotificationSettingsEvents.FixConfigurationMismatch) + initialState.eventSink(NotificationSettingsEvent.FixConfigurationMismatch) val fixedState = consumeItemsUntilPredicate(timeout = 2000.milliseconds) { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() @@ -148,14 +148,14 @@ class NotificationSettingsPresenterTest { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() assertThat(loadedState.appSettings.appNotificationsEnabled).isTrue() - loadedState.eventSink(NotificationSettingsEvents.SetNotificationsEnabled(false)) + loadedState.eventSink(NotificationSettingsEvent.SetNotificationsEnabled(false)) val updatedState = consumeItemsUntilPredicate { !it.appSettings.appNotificationsEnabled }.last() assertThat(updatedState.appSettings.appNotificationsEnabled).isFalse() unregisterWithResult.assertions().isCalledOnce() // Enable notification again - loadedState.eventSink(NotificationSettingsEvents.SetNotificationsEnabled(true)) + loadedState.eventSink(NotificationSettingsEvent.SetNotificationsEnabled(true)) val updatedState2 = consumeItemsUntilPredicate { it.appSettings.appNotificationsEnabled }.last() @@ -173,7 +173,7 @@ class NotificationSettingsPresenterTest { }.last() val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid assertThat(validMatrixState?.callNotificationsEnabled).isFalse() - loadedState.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(true)) + loadedState.eventSink(NotificationSettingsEvent.SetCallNotificationsEnabled(true)) val updatedState = consumeItemsUntilPredicate { (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.callNotificationsEnabled == true }.last() @@ -192,7 +192,7 @@ class NotificationSettingsPresenterTest { }.last() val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid assertThat(validMatrixState?.inviteForMeNotificationsEnabled).isFalse() - loadedState.eventSink(NotificationSettingsEvents.SetInviteForMeNotificationsEnabled(true)) + loadedState.eventSink(NotificationSettingsEvent.SetInviteForMeNotificationsEnabled(true)) val updatedState = consumeItemsUntilPredicate { (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.inviteForMeNotificationsEnabled == true }.last() @@ -211,7 +211,7 @@ class NotificationSettingsPresenterTest { }.last() val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid assertThat(validMatrixState?.atRoomNotificationsEnabled).isFalse() - loadedState.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(true)) + loadedState.eventSink(NotificationSettingsEvent.SetAtRoomNotificationsEnabled(true)) val updatedState = consumeItemsUntilPredicate { (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.atRoomNotificationsEnabled == true }.last() @@ -232,12 +232,12 @@ class NotificationSettingsPresenterTest { }.last() val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid assertThat(validMatrixState?.atRoomNotificationsEnabled).isFalse() - loadedState.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(true)) + loadedState.eventSink(NotificationSettingsEvent.SetAtRoomNotificationsEnabled(true)) val errorState = consumeItemsUntilPredicate { it.changeNotificationSettingAction.isFailure() }.last() assertThat(errorState.changeNotificationSettingAction.isFailure()).isTrue() - errorState.eventSink(NotificationSettingsEvents.ClearNotificationChangeError) + errorState.eventSink(NotificationSettingsEvent.ClearNotificationChangeError) val clearErrorState = consumeItemsUntilPredicate { it.changeNotificationSettingAction.isUninitialized() }.last() @@ -258,16 +258,16 @@ class NotificationSettingsPresenterTest { Distributor(value = "aDistributorValue0", name = "aDistributorName0"), Distributor(value = "aDistributorValue1", name = "aDistributorName1"), ) - initialState.eventSink.invoke(NotificationSettingsEvents.ChangePushProvider) + initialState.eventSink.invoke(NotificationSettingsEvent.ChangePushProvider) val withDialog = awaitItem() assertThat(withDialog.showChangePushProviderDialog).isTrue() // Cancel - withDialog.eventSink(NotificationSettingsEvents.CancelChangePushProvider) + withDialog.eventSink(NotificationSettingsEvent.CancelChangePushProvider) val withoutDialog = awaitItem() assertThat(withoutDialog.showChangePushProviderDialog).isFalse() - withDialog.eventSink.invoke(NotificationSettingsEvents.ChangePushProvider) + withDialog.eventSink.invoke(NotificationSettingsEvent.ChangePushProvider) assertThat(awaitItem().showChangePushProviderDialog).isTrue() - withDialog.eventSink(NotificationSettingsEvents.SetPushProvider(1)) + withDialog.eventSink(NotificationSettingsEvent.SetPushProvider(1)) val withNewProvider = awaitItem() assertThat(withNewProvider.showChangePushProviderDialog).isFalse() assertThat(withNewProvider.currentPushDistributor).isInstanceOf(AsyncData.Loading::class.java) @@ -290,10 +290,10 @@ class NotificationSettingsPresenterTest { Distributor(value = "aDistributorValue0", name = "aDistributorName0"), Distributor(value = "aDistributorValue1", name = "aDistributorName1"), ) - initialState.eventSink.invoke(NotificationSettingsEvents.ChangePushProvider) + initialState.eventSink.invoke(NotificationSettingsEvent.ChangePushProvider) assertThat(awaitItem().showChangePushProviderDialog).isTrue() // Choose the same value (index 0) - initialState.eventSink(NotificationSettingsEvents.SetPushProvider(0)) + initialState.eventSink(NotificationSettingsEvent.SetPushProvider(0)) val withNewProvider = awaitItem() assertThat(withNewProvider.showChangePushProviderDialog).isFalse() expectNoEvents() @@ -318,7 +318,7 @@ class NotificationSettingsPresenterTest { expectNoEvents() // Refresh - initialState.eventSink.invoke(NotificationSettingsEvents.RefreshSystemNotificationsEnabled) + initialState.eventSink.invoke(NotificationSettingsEvent.RefreshSystemNotificationsEnabled) assertThat(awaitItem().fullScreenIntentPermissionsState.permissionGranted).isTrue() } } @@ -334,10 +334,10 @@ class NotificationSettingsPresenterTest { ) presenter.test { val initialState = awaitLastSequentialItem() - initialState.eventSink.invoke(NotificationSettingsEvents.ChangePushProvider) + initialState.eventSink.invoke(NotificationSettingsEvent.ChangePushProvider) val withDialog = awaitItem() assertThat(withDialog.showChangePushProviderDialog).isTrue() - withDialog.eventSink(NotificationSettingsEvents.SetPushProvider(1)) + withDialog.eventSink(NotificationSettingsEvent.SetPushProvider(1)) val withNewProvider = awaitItem() assertThat(withNewProvider.showChangePushProviderDialog).isFalse() assertThat(withNewProvider.currentPushDistributor).isInstanceOf(AsyncData.Loading::class.java) @@ -384,7 +384,7 @@ class NotificationSettingsPresenterTest { }.last() assertThat(initialState.messageSound.sound).isEqualTo(NotificationSound.SystemDefault) - initialState.eventSink(NotificationSettingsEvents.SetMessageSound(customSound)) + initialState.eventSink(NotificationSettingsEvent.SetMessageSound(customSound)) val updatedState = consumeItemsUntilPredicate { it.messageSound.sound == customSound }.last() assertThat(updatedState.messageSound.sound).isEqualTo(customSound) @@ -410,7 +410,7 @@ class NotificationSettingsPresenterTest { }.last() assertThat(initialState.callRingtone.sound).isEqualTo(NotificationSound.SystemDefault) - initialState.eventSink(NotificationSettingsEvents.SetCallRingtone(NotificationSound.Silent)) + initialState.eventSink(NotificationSettingsEvent.SetCallRingtone(NotificationSound.Silent)) val updatedState = consumeItemsUntilPredicate { it.callRingtone.sound == NotificationSound.Silent }.last() assertThat(updatedState.callRingtone.sound).isEqualTo(NotificationSound.Silent) @@ -435,11 +435,11 @@ class NotificationSettingsPresenterTest { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() - initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.Custom("content://a"))) + initialState.eventSink(NotificationSettingsEvent.SetMessageSound(NotificationSound.Custom("content://a"))) consumeItemsUntilPredicate { it.messageSound.sound == NotificationSound.Custom("content://a") } - initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.Custom("content://b"))) + initialState.eventSink(NotificationSettingsEvent.SetMessageSound(NotificationSound.Custom("content://b"))) consumeItemsUntilPredicate { it.messageSound.sound == NotificationSound.Custom("content://b") } - initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.SystemDefault)) + initialState.eventSink(NotificationSettingsEvent.SetMessageSound(NotificationSound.SystemDefault)) consumeItemsUntilPredicate { it.messageSound.sound == NotificationSound.SystemDefault } assertThat(versions).containsExactly(1, 2, 3).inOrder() @@ -461,7 +461,7 @@ class NotificationSettingsPresenterTest { consumeItemsUntilPredicate { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid && it.messageSound.sound == NotificationSound.Custom("content://existing") - }.last().eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.SystemDefault)) + }.last().eventSink(NotificationSettingsEvent.SetMessageSound(NotificationSound.SystemDefault)) consumeItemsUntilPredicate { it.messageSound.sound == NotificationSound.SystemDefault } assertThat(appPreferencesStore.getNotificationSoundChannelConfig().messageSound).isEqualTo(NotificationSound.SystemDefault) recreateRecorder.assertions().isCalledOnce().with(value(NotificationSound.SystemDefault), value(1)) @@ -483,7 +483,7 @@ class NotificationSettingsPresenterTest { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() - initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.Custom("content://x"))) + initialState.eventSink(NotificationSettingsEvent.SetMessageSound(NotificationSound.Custom("content://x"))) val errorState = consumeItemsUntilPredicate { it.changeNotificationSettingAction.isFailure() @@ -670,7 +670,7 @@ class NotificationSettingsPresenterTest { val initialState = consumeItemsUntilPredicate { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() - initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.Custom("content://media/source"))) + initialState.eventSink(NotificationSettingsEvent.SetMessageSound(NotificationSound.Custom("content://media/source"))) advanceUntilIdle() val expectedSound = NotificationSound.Custom("content://my.app.fileprovider/notification_sounds/message_sound.ogg") @@ -695,7 +695,7 @@ class NotificationSettingsPresenterTest { val initialState = consumeItemsUntilPredicate { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() - initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.Custom("content://broken"))) + initialState.eventSink(NotificationSettingsEvent.SetMessageSound(NotificationSound.Custom("content://broken"))) val errorState = consumeItemsUntilPredicate { it.messageSound.copyError }.last() assertThat(errorState.messageSound.copyError).isTrue() @@ -703,7 +703,7 @@ class NotificationSettingsPresenterTest { assertThat(appPreferencesStore.getNotificationSoundChannelConfig().messageSound).isEqualTo(priorChoice) // Dismissing the inline error clears the flag without altering persistence. - errorState.eventSink(NotificationSettingsEvents.DismissMessageSoundCopyError) + errorState.eventSink(NotificationSettingsEvent.DismissMessageSoundCopyError) val cleared = consumeItemsUntilPredicate { !it.messageSound.copyError }.last() assertThat(cleared.messageSound.copyError).isFalse() assertThat(appPreferencesStore.getNotificationSoundChannelConfig().messageSound).isEqualTo(priorChoice) @@ -725,7 +725,7 @@ class NotificationSettingsPresenterTest { val initialState = consumeItemsUntilPredicate { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() - initialState.eventSink(NotificationSettingsEvents.SetCallRingtone(NotificationSound.Custom("content://broken"))) + initialState.eventSink(NotificationSettingsEvent.SetCallRingtone(NotificationSound.Custom("content://broken"))) val errorState = consumeItemsUntilPredicate { it.callRingtone.copyError }.last() assertThat(errorState.callRingtone.copyError).isTrue() @@ -733,7 +733,7 @@ class NotificationSettingsPresenterTest { assertThat(appPreferencesStore.getNotificationSoundChannelConfig().callRingtone).isEqualTo(priorChoice) // Dismissing the inline error clears the flag without altering persistence. - errorState.eventSink(NotificationSettingsEvents.DismissCallRingtoneCopyError) + errorState.eventSink(NotificationSettingsEvent.DismissCallRingtoneCopyError) val cleared = consumeItemsUntilPredicate { !it.callRingtone.copyError }.last() assertThat(cleared.callRingtone.copyError).isFalse() assertThat(appPreferencesStore.getNotificationSoundChannelConfig().callRingtone).isEqualTo(priorChoice) @@ -759,8 +759,8 @@ class NotificationSettingsPresenterTest { val initialState = consumeItemsUntilPredicate { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() - initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.SystemDefault)) - initialState.eventSink(NotificationSettingsEvents.SetCallRingtone(NotificationSound.Silent)) + initialState.eventSink(NotificationSettingsEvent.SetMessageSound(NotificationSound.SystemDefault)) + initialState.eventSink(NotificationSettingsEvent.SetCallRingtone(NotificationSound.Silent)) advanceUntilIdle() assertThat(copierCalls).isEmpty() cancelAndIgnoreRemainingEvents() @@ -791,8 +791,8 @@ class NotificationSettingsPresenterTest { val initialState = consumeItemsUntilPredicate { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() - initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.SystemDefault)) - initialState.eventSink(NotificationSettingsEvents.SetCallRingtone(NotificationSound.Silent)) + initialState.eventSink(NotificationSettingsEvent.SetMessageSound(NotificationSound.SystemDefault)) + initialState.eventSink(NotificationSettingsEvent.SetCallRingtone(NotificationSound.Silent)) advanceUntilIdle() assertThat(deleteCalls).containsExactly( NotificationSoundCopier.SoundSlot.Message, @@ -835,8 +835,8 @@ class NotificationSettingsPresenterTest { // Re-pick the persisted sound for both slots — neither should churn the channel, // bump the version, or hit the copier / delete sweep. - initial.eventSink(NotificationSettingsEvents.SelectMessageSoundPreset(NotificationSound.ElementDefault)) - initial.eventSink(NotificationSettingsEvents.SetCallRingtone(NotificationSound.Silent)) + initial.eventSink(NotificationSettingsEvent.SelectMessageSoundPreset(NotificationSound.ElementDefault)) + initial.eventSink(NotificationSettingsEvent.SetCallRingtone(NotificationSound.Silent)) advanceUntilIdle() val config = appPreferencesStore.getNotificationSoundChannelConfig() @@ -873,8 +873,8 @@ class NotificationSettingsPresenterTest { val initial = consumeItemsUntilPredicate { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() - initial.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.Custom("content://A"))) - initial.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.Custom("content://B"))) + initial.eventSink(NotificationSettingsEvent.SetMessageSound(NotificationSound.Custom("content://A"))) + initial.eventSink(NotificationSettingsEvent.SetMessageSound(NotificationSound.Custom("content://B"))) advanceUntilIdle() assertThat(copyOrder).containsExactly("content://A", "content://B").inOrder() @@ -908,7 +908,7 @@ class NotificationSettingsPresenterTest { val initialState = consumeItemsUntilPredicate { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() - initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.Custom("content://media/source"))) + initialState.eventSink(NotificationSettingsEvent.SetMessageSound(NotificationSound.Custom("content://media/source"))) advanceUntilIdle() assertThat(deleteCalls).isEmpty() cancelAndIgnoreRemainingEvents() @@ -924,11 +924,11 @@ class NotificationSettingsPresenterTest { }.last() assertThat(initial.showMessageSoundDialog).isFalse() - initial.eventSink(NotificationSettingsEvents.ShowMessageSoundDialog) + initial.eventSink(NotificationSettingsEvent.ShowMessageSoundDialog) val shown = consumeItemsUntilPredicate { it.showMessageSoundDialog }.last() assertThat(shown.showMessageSoundDialog).isTrue() - shown.eventSink(NotificationSettingsEvents.DismissMessageSoundDialog) + shown.eventSink(NotificationSettingsEvent.DismissMessageSoundDialog) val hidden = consumeItemsUntilPredicate { !it.showMessageSoundDialog }.last() assertThat(hidden.showMessageSoundDialog).isFalse() cancelAndIgnoreRemainingEvents() @@ -950,10 +950,10 @@ class NotificationSettingsPresenterTest { val initial = consumeItemsUntilPredicate { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() - initial.eventSink(NotificationSettingsEvents.ShowMessageSoundDialog) + initial.eventSink(NotificationSettingsEvent.ShowMessageSoundDialog) val shown = consumeItemsUntilPredicate { it.showMessageSoundDialog }.last() - shown.eventSink(NotificationSettingsEvents.SelectMessageSoundPreset(NotificationSound.ElementDefault)) + shown.eventSink(NotificationSettingsEvent.SelectMessageSoundPreset(NotificationSound.ElementDefault)) advanceUntilIdle() consumeItemsUntilPredicate { !it.showMessageSoundDialog && it.messageSound.sound == NotificationSound.ElementDefault @@ -973,10 +973,10 @@ class NotificationSettingsPresenterTest { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() assertThat(initial.pendingMessageSoundPickerLaunch).isEqualTo(0) - initial.eventSink(NotificationSettingsEvents.ShowMessageSoundDialog) + initial.eventSink(NotificationSettingsEvent.ShowMessageSoundDialog) val shown = consumeItemsUntilPredicate { it.showMessageSoundDialog }.last() - shown.eventSink(NotificationSettingsEvents.LaunchMessageSoundPicker) + shown.eventSink(NotificationSettingsEvent.LaunchMessageSoundPicker) val launched = consumeItemsUntilPredicate { it.pendingMessageSoundPickerLaunch == 1 }.last() assertThat(launched.showMessageSoundDialog).isFalse() assertThat(launched.pendingMessageSoundPickerLaunch).isEqualTo(1) @@ -993,11 +993,11 @@ class NotificationSettingsPresenterTest { }.last() assertThat(initial.showCallRingtoneDialog).isFalse() - initial.eventSink(NotificationSettingsEvents.ShowCallRingtoneDialog) + initial.eventSink(NotificationSettingsEvent.ShowCallRingtoneDialog) val shown = consumeItemsUntilPredicate { it.showCallRingtoneDialog }.last() assertThat(shown.showCallRingtoneDialog).isTrue() - shown.eventSink(NotificationSettingsEvents.DismissCallRingtoneDialog) + shown.eventSink(NotificationSettingsEvent.DismissCallRingtoneDialog) val hidden = consumeItemsUntilPredicate { !it.showCallRingtoneDialog }.last() assertThat(hidden.showCallRingtoneDialog).isFalse() cancelAndIgnoreRemainingEvents() @@ -1020,10 +1020,10 @@ class NotificationSettingsPresenterTest { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid && it.callRingtone.sound == NotificationSound.Silent }.last() - initial.eventSink(NotificationSettingsEvents.ShowCallRingtoneDialog) + initial.eventSink(NotificationSettingsEvent.ShowCallRingtoneDialog) val shown = consumeItemsUntilPredicate { it.showCallRingtoneDialog }.last() - shown.eventSink(NotificationSettingsEvents.SelectCallRingtonePreset(NotificationSound.SystemDefault)) + shown.eventSink(NotificationSettingsEvent.SelectCallRingtonePreset(NotificationSound.SystemDefault)) advanceUntilIdle() consumeItemsUntilPredicate { !it.showCallRingtoneDialog && it.callRingtone.sound == NotificationSound.SystemDefault @@ -1043,10 +1043,10 @@ class NotificationSettingsPresenterTest { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() assertThat(initial.pendingCallRingtonePickerLaunch).isEqualTo(0) - initial.eventSink(NotificationSettingsEvents.ShowCallRingtoneDialog) + initial.eventSink(NotificationSettingsEvent.ShowCallRingtoneDialog) val shown = consumeItemsUntilPredicate { it.showCallRingtoneDialog }.last() - shown.eventSink(NotificationSettingsEvents.LaunchCallRingtonePicker) + shown.eventSink(NotificationSettingsEvent.LaunchCallRingtonePicker) val launched = consumeItemsUntilPredicate { it.pendingCallRingtonePickerLaunch == 1 }.last() assertThat(launched.showCallRingtoneDialog).isFalse() assertThat(launched.pendingCallRingtonePickerLaunch).isEqualTo(1) @@ -1078,7 +1078,7 @@ class NotificationSettingsPresenterTest { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid && it.callRingtone.sound == NotificationSound.Silent }.last() - initial.eventSink(NotificationSettingsEvents.SelectCallRingtonePreset(NotificationSound.SystemDefault)) + initial.eventSink(NotificationSettingsEvent.SelectCallRingtonePreset(NotificationSound.SystemDefault)) advanceUntilIdle() val after = consumeItemsUntilPredicate { it.callRingtone.sound == NotificationSound.SystemDefault diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsViewTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsViewTest.kt index 57edf01a1d..a28f43638f 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsViewTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsViewTest.kt @@ -35,7 +35,7 @@ import org.robolectric.annotation.Config class NotificationSettingsViewTest : RobolectricTest() { @Test fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() ensureCalledOnce { setNotificationSettingsView( state = aValidNotificationSettingsState( @@ -45,13 +45,13 @@ class NotificationSettingsViewTest : RobolectricTest() { ) pressBack() } - eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled) + eventsRecorder.assertSingle(NotificationSettingsEvent.RefreshSystemNotificationsEnabled) } @Config(qualifiers = "h1280dp") @Test fun `clicking on troubleshoot notification invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() ensureCalledOnce { setNotificationSettingsView( state = aValidNotificationSettingsState( @@ -61,13 +61,13 @@ class NotificationSettingsViewTest : RobolectricTest() { ) clickOn(R.string.troubleshoot_notifications_entry_point_title) } - eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled) + eventsRecorder.assertSingle(NotificationSettingsEvent.RefreshSystemNotificationsEnabled) } @Config(qualifiers = "h1024dp") @Test fun `clicking on group chats invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() ensureCalledOnceWithParam(false) { setNotificationSettingsView( state = aValidNotificationSettingsState( @@ -77,13 +77,13 @@ class NotificationSettingsViewTest : RobolectricTest() { ) clickOn(R.string.screen_notification_settings_group_chats) } - eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled) + eventsRecorder.assertSingle(NotificationSettingsEvent.RefreshSystemNotificationsEnabled) } @Config(qualifiers = "h1024dp") @Test fun `clicking on direct chats invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() ensureCalledOnceWithParam(true) { setNotificationSettingsView( state = aValidNotificationSettingsState( @@ -93,7 +93,7 @@ class NotificationSettingsViewTest : RobolectricTest() { ) clickOn(R.string.screen_notification_settings_direct_chats) } - eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled) + eventsRecorder.assertSingle(NotificationSettingsEvent.RefreshSystemNotificationsEnabled) } @Config(qualifiers = "h1024dp") @@ -109,7 +109,7 @@ class NotificationSettingsViewTest : RobolectricTest() { } private fun testNotificationToggle(initialState: Boolean) = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setNotificationSettingsView( state = aValidNotificationSettingsState( appNotificationEnabled = initialState, @@ -119,8 +119,8 @@ class NotificationSettingsViewTest : RobolectricTest() { clickOn(R.string.screen_notification_settings_enable_notifications) eventsRecorder.assertList( listOf( - NotificationSettingsEvents.RefreshSystemNotificationsEnabled, - NotificationSettingsEvents.SetNotificationsEnabled(!initialState) + NotificationSettingsEvent.RefreshSystemNotificationsEnabled, + NotificationSettingsEvent.SetNotificationsEnabled(!initialState) ) ) } @@ -138,7 +138,7 @@ class NotificationSettingsViewTest : RobolectricTest() { } private fun testAtRoomToggle(initialState: Boolean) = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setNotificationSettingsView( state = aValidNotificationSettingsState( atRoomNotificationsEnabled = initialState, @@ -148,8 +148,8 @@ class NotificationSettingsViewTest : RobolectricTest() { clickOn(R.string.screen_notification_settings_room_mention_label) eventsRecorder.assertList( listOf( - NotificationSettingsEvents.RefreshSystemNotificationsEnabled, - NotificationSettingsEvents.SetAtRoomNotificationsEnabled(!initialState) + NotificationSettingsEvent.RefreshSystemNotificationsEnabled, + NotificationSettingsEvent.SetAtRoomNotificationsEnabled(!initialState) ) ) } @@ -167,7 +167,7 @@ class NotificationSettingsViewTest : RobolectricTest() { } private fun testInvitationToggle(initialState: Boolean) = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setNotificationSettingsView( state = aValidNotificationSettingsState( inviteForMeNotificationsEnabled = initialState, @@ -177,8 +177,8 @@ class NotificationSettingsViewTest : RobolectricTest() { clickOn(R.string.screen_notification_settings_invite_for_me_label) eventsRecorder.assertList( listOf( - NotificationSettingsEvents.RefreshSystemNotificationsEnabled, - NotificationSettingsEvents.SetInviteForMeNotificationsEnabled(!initialState) + NotificationSettingsEvent.RefreshSystemNotificationsEnabled, + NotificationSettingsEvent.SetInviteForMeNotificationsEnabled(!initialState) ) ) } @@ -186,7 +186,7 @@ class NotificationSettingsViewTest : RobolectricTest() { @Config(qualifiers = "h1024dp") @Test fun `with an error configuration, clicking on continue emits the expected events`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setNotificationSettingsView( state = aValidNotificationSettingsState( changeNotificationSettingAction = AsyncAction.Failure(AN_EXCEPTION), @@ -196,8 +196,8 @@ class NotificationSettingsViewTest : RobolectricTest() { clickOn(CommonStrings.action_ok) eventsRecorder.assertList( listOf( - NotificationSettingsEvents.RefreshSystemNotificationsEnabled, - NotificationSettingsEvents.ClearNotificationChangeError + NotificationSettingsEvent.RefreshSystemNotificationsEnabled, + NotificationSettingsEvent.ClearNotificationChangeError ) ) } @@ -205,7 +205,7 @@ class NotificationSettingsViewTest : RobolectricTest() { @Config(qualifiers = "h1024dp") @Test fun `with invalid configuration, clicking on continue emits the expected events`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setNotificationSettingsView( state = aInvalidNotificationSettingsState( fixFailed = false, @@ -215,8 +215,8 @@ class NotificationSettingsViewTest : RobolectricTest() { clickOn(CommonStrings.action_continue) eventsRecorder.assertList( listOf( - NotificationSettingsEvents.RefreshSystemNotificationsEnabled, - NotificationSettingsEvents.FixConfigurationMismatch + NotificationSettingsEvent.RefreshSystemNotificationsEnabled, + NotificationSettingsEvent.FixConfigurationMismatch ) ) } @@ -224,7 +224,7 @@ class NotificationSettingsViewTest : RobolectricTest() { @Config(qualifiers = "h1024dp") @Test fun `with invalid configuration and error, clicking on OK emits the expected events`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setNotificationSettingsView( state = aInvalidNotificationSettingsState( fixFailed = true, @@ -234,8 +234,8 @@ class NotificationSettingsViewTest : RobolectricTest() { clickOn(CommonStrings.action_ok) eventsRecorder.assertList( listOf( - NotificationSettingsEvents.RefreshSystemNotificationsEnabled, - NotificationSettingsEvents.ClearConfigurationMismatchError + NotificationSettingsEvent.RefreshSystemNotificationsEnabled, + NotificationSettingsEvent.ClearConfigurationMismatchError ) ) } @@ -243,7 +243,7 @@ class NotificationSettingsViewTest : RobolectricTest() { @Config(qualifiers = "h1280dp") @Test fun `clicking on Push notification provider emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setNotificationSettingsView( state = aValidNotificationSettingsState( eventSink = eventsRecorder @@ -252,15 +252,15 @@ class NotificationSettingsViewTest : RobolectricTest() { clickOn(R.string.screen_advanced_settings_push_provider_android) eventsRecorder.assertList( listOf( - NotificationSettingsEvents.RefreshSystemNotificationsEnabled, - NotificationSettingsEvents.ChangePushProvider, + NotificationSettingsEvent.RefreshSystemNotificationsEnabled, + NotificationSettingsEvent.ChangePushProvider, ) ) } @Test fun `clicking on a push provider emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setNotificationSettingsView( state = aValidNotificationSettingsState( eventSink = eventsRecorder, @@ -271,8 +271,8 @@ class NotificationSettingsViewTest : RobolectricTest() { onNodeWithText("P2").performClick() eventsRecorder.assertList( listOf( - NotificationSettingsEvents.RefreshSystemNotificationsEnabled, - NotificationSettingsEvents.SetPushProvider(1), + NotificationSettingsEvent.RefreshSystemNotificationsEnabled, + NotificationSettingsEvent.SetPushProvider(1), ) ) } @@ -280,7 +280,7 @@ class NotificationSettingsViewTest : RobolectricTest() { @Config(qualifiers = "h1280dp") @Test fun `sounds preference category renders rows with current display names`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setNotificationSettingsView( state = aValidNotificationSettingsState( eventSink = eventsRecorder, @@ -298,7 +298,7 @@ class NotificationSettingsViewTest : RobolectricTest() { @Config(qualifiers = "h1280dp") @Test fun `clicking the message sound row opens the preset dialog`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setNotificationSettingsView( state = aValidNotificationSettingsState(eventSink = eventsRecorder), ) @@ -308,8 +308,8 @@ class NotificationSettingsViewTest : RobolectricTest() { onNodeWithText("Message sound").performClick() eventsRecorder.assertList( listOf( - NotificationSettingsEvents.RefreshSystemNotificationsEnabled, - NotificationSettingsEvents.ShowMessageSoundDialog, + NotificationSettingsEvent.RefreshSystemNotificationsEnabled, + NotificationSettingsEvent.ShowMessageSoundDialog, ) ) } From 5d8ca663df79b090cc7a355e47288a2448a48377 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:12:06 +0200 Subject: [PATCH 044/112] `EditDefaultNotificationSettingStateEvents` -> `EditDefaultNotificationSettingStateEvent` --- .../edit/EditDefaultNotificationSettingPresenter.kt | 6 +++--- .../edit/EditDefaultNotificationSettingState.kt | 2 +- ...nts.kt => EditDefaultNotificationSettingStateEvent.kt} | 6 +++--- .../edit/EditDefaultNotificationSettingView.kt | 4 ++-- .../EditDefaultNotificationSettingsPresenterTest.kt | 8 ++++---- 5 files changed, 13 insertions(+), 13 deletions(-) rename features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/{EditDefaultNotificationSettingStateEvents.kt => EditDefaultNotificationSettingStateEvent.kt} (81%) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt index f2cb8b02cc..5769a8abe2 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt @@ -76,12 +76,12 @@ class EditDefaultNotificationSettingPresenter( displayMentionsOnlyDisclaimer = !notificationSettingsService.canHomeServerPushEncryptedEventsToDevice().getOrDefault(true) } - fun handleEvent(event: EditDefaultNotificationSettingStateEvents) { + fun handleEvent(event: EditDefaultNotificationSettingStateEvent) { when (event) { - is EditDefaultNotificationSettingStateEvents.SetNotificationMode -> { + is EditDefaultNotificationSettingStateEvent.SetNotificationMode -> { localCoroutineScope.setDefaultNotificationMode(event.mode, changeNotificationSettingAction) } - EditDefaultNotificationSettingStateEvents.ClearError -> changeNotificationSettingAction.value = AsyncAction.Uninitialized + EditDefaultNotificationSettingStateEvent.ClearError -> changeNotificationSettingAction.value = AsyncAction.Uninitialized } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt index 17c8cad95c..840f62eb4f 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt @@ -18,5 +18,5 @@ data class EditDefaultNotificationSettingState( val roomsWithUserDefinedMode: ImmutableList, val changeNotificationSettingAction: AsyncAction, val displayMentionsOnlyDisclaimer: Boolean, - val eventSink: (EditDefaultNotificationSettingStateEvents) -> Unit, + val eventSink: (EditDefaultNotificationSettingStateEvent) -> Unit, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateEvent.kt similarity index 81% rename from features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateEvents.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateEvent.kt index a7993c118a..4ede5813c1 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateEvent.kt @@ -10,7 +10,7 @@ package io.element.android.features.preferences.impl.notifications.edit import io.element.android.libraries.matrix.api.room.RoomNotificationMode -sealed interface EditDefaultNotificationSettingStateEvents { - data class SetNotificationMode(val mode: RoomNotificationMode) : EditDefaultNotificationSettingStateEvents - data object ClearError : EditDefaultNotificationSettingStateEvents +sealed interface EditDefaultNotificationSettingStateEvent { + data class SetNotificationMode(val mode: RoomNotificationMode) : EditDefaultNotificationSettingStateEvent + data object ClearError : EditDefaultNotificationSettingStateEvent } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt index 3d4256f65d..9bfde12513 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt @@ -70,7 +70,7 @@ fun EditDefaultNotificationSettingView( mode = item, isSelected = state.mode == item, displayMentionsOnlyDisclaimer = state.displayMentionsOnlyDisclaimer, - onSelectOption = { state.eventSink(EditDefaultNotificationSettingStateEvents.SetNotificationMode(it)) } + onSelectOption = { state.eventSink(EditDefaultNotificationSettingStateEvent.SetNotificationMode(it)) } ) } } @@ -116,7 +116,7 @@ fun EditDefaultNotificationSettingView( AsyncActionView( async = state.changeNotificationSettingAction, errorMessage = { stringResource(R.string.screen_notification_settings_edit_failed_updating_default_mode) }, - onErrorDismiss = { state.eventSink(EditDefaultNotificationSettingStateEvents.ClearError) }, + onErrorDismiss = { state.eventSink(EditDefaultNotificationSettingStateEvent.ClearError) }, onSuccess = {}, ) } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTest.kt index 509d00db25..10826071e7 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTest.kt @@ -10,7 +10,7 @@ package io.element.android.features.preferences.impl.notifications import com.google.common.truth.Truth.assertThat import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingPresenter -import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingStateEvents +import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingStateEvent import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_ROOM_ID @@ -150,7 +150,7 @@ class EditDefaultNotificationSettingsPresenterTest { ), ) presenter.test { - awaitItem().eventSink(EditDefaultNotificationSettingStateEvents.SetNotificationMode(RoomNotificationMode.ALL_MESSAGES)) + awaitItem().eventSink(EditDefaultNotificationSettingStateEvent.SetNotificationMode(RoomNotificationMode.ALL_MESSAGES)) val loadedState = consumeItemsUntilPredicate { it.mode == RoomNotificationMode.ALL_MESSAGES }.last() @@ -166,12 +166,12 @@ class EditDefaultNotificationSettingsPresenterTest { val presenter = createEditDefaultNotificationSettingPresenter(notificationSettingsService) notificationSettingsService.givenSetDefaultNotificationModeError(AN_EXCEPTION) presenter.test { - awaitItem().eventSink(EditDefaultNotificationSettingStateEvents.SetNotificationMode(RoomNotificationMode.ALL_MESSAGES)) + awaitItem().eventSink(EditDefaultNotificationSettingStateEvent.SetNotificationMode(RoomNotificationMode.ALL_MESSAGES)) val errorState = consumeItemsUntilPredicate { it.changeNotificationSettingAction.isFailure() }.last() assertThat(errorState.changeNotificationSettingAction.isFailure()).isTrue() - errorState.eventSink(EditDefaultNotificationSettingStateEvents.ClearError) + errorState.eventSink(EditDefaultNotificationSettingStateEvent.ClearError) val clearErrorState = consumeItemsUntilPredicate { it.changeNotificationSettingAction.isUninitialized() }.last() From 859b0e1b60cd876e8a2f901a5f6ef635c3f1df77 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:13:33 +0200 Subject: [PATCH 045/112] `BugReportEvents` -> `BugReportEvent` --- .../{BugReportEvents.kt => BugReportEvent.kt} | 20 ++++---- .../impl/bugreport/BugReportPresenter.kt | 20 ++++---- .../impl/bugreport/BugReportState.kt | 2 +- .../rageshake/impl/bugreport/BugReportView.kt | 20 ++++---- .../impl/bugreport/BugReportPresenterTest.kt | 46 +++++++++---------- 5 files changed, 54 insertions(+), 54 deletions(-) rename features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/{BugReportEvents.kt => BugReportEvent.kt} (74%) diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportEvents.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportEvent.kt similarity index 74% rename from features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportEvents.kt rename to features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportEvent.kt index 94897d1bad..448f5a9d87 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportEvents.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportEvent.kt @@ -8,15 +8,15 @@ package io.element.android.features.rageshake.impl.bugreport -sealed interface BugReportEvents { - data object SendBugReport : BugReportEvents - data object ResetAll : BugReportEvents - data object ClearError : BugReportEvents +sealed interface BugReportEvent { + data object SendBugReport : BugReportEvent + data object ResetAll : BugReportEvent + data object ClearError : BugReportEvent - data class SetDescription(val description: String) : BugReportEvents - data class SetSendLog(val sendLog: Boolean) : BugReportEvents - data class SetCanContact(val canContact: Boolean) : BugReportEvents - data class SetSendScreenshot(val sendScreenshot: Boolean) : BugReportEvents - data class SetSendPushRules(val sendPushRules: Boolean) : BugReportEvents - data class SetGhIssueNumber(val ghIssueNumber: Int?) : BugReportEvents + data class SetDescription(val description: String) : BugReportEvent + data class SetSendLog(val sendLog: Boolean) : BugReportEvent + data class SetCanContact(val canContact: Boolean) : BugReportEvent + data class SetSendScreenshot(val sendScreenshot: Boolean) : BugReportEvent + data class SetSendPushRules(val sendPushRules: Boolean) : BugReportEvent + data class SetGhIssueNumber(val ghIssueNumber: Int?) : BugReportEvent } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt index c7c706fa68..607653baa4 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt @@ -83,9 +83,9 @@ class BugReportPresenter( } val uploadListener = BugReporterUploadListener(sendingProgress, sendingAction) - fun handleEvent(event: BugReportEvents) { + fun handleEvent(event: BugReportEvent) { when (event) { - BugReportEvents.SendBugReport -> { + BugReportEvent.SendBugReport -> { if (formState.value.description.length < 10 && formState.value.ghIssueNumber == null) { sendingAction.value = AsyncAction.Failure(BugReportFormError.DescriptionTooShort) } else { @@ -93,27 +93,27 @@ class BugReportPresenter( appCoroutineScope.sendBugReport(formState.value, crashInfo.isNotEmpty(), uploadListener) } } - BugReportEvents.ResetAll -> appCoroutineScope.resetAll() - is BugReportEvents.SetDescription -> updateFormState(formState) { + BugReportEvent.ResetAll -> appCoroutineScope.resetAll() + is BugReportEvent.SetDescription -> updateFormState(formState) { copy(description = event.description) } - is BugReportEvents.SetCanContact -> updateFormState(formState) { + is BugReportEvent.SetCanContact -> updateFormState(formState) { copy(canContact = event.canContact) } - is BugReportEvents.SetSendLog -> updateFormState(formState) { + is BugReportEvent.SetSendLog -> updateFormState(formState) { copy(sendLogs = event.sendLog) } - is BugReportEvents.SetSendScreenshot -> updateFormState(formState) { + is BugReportEvent.SetSendScreenshot -> updateFormState(formState) { copy(sendScreenshot = event.sendScreenshot) } - is BugReportEvents.SetSendPushRules -> updateFormState(formState) { + is BugReportEvent.SetSendPushRules -> updateFormState(formState) { copy(sendPushRules = event.sendPushRules) } - BugReportEvents.ClearError -> { + BugReportEvent.ClearError -> { sendingProgress.floatValue = 0f sendingAction.value = AsyncAction.Uninitialized } - is BugReportEvents.SetGhIssueNumber -> updateFormState(formState) { + is BugReportEvent.SetGhIssueNumber -> updateFormState(formState) { copy(ghIssueNumber = event.ghIssueNumber) } } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportState.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportState.kt index 8e07b3c4e8..4ec5710cc6 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportState.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportState.kt @@ -18,7 +18,7 @@ data class BugReportState( val screenshotUri: String?, val sendingProgress: Float, val sending: AsyncAction, - val eventSink: (BugReportEvents) -> Unit + val eventSink: (BugReportEvent) -> Unit ) { val submitEnabled = sending !is AsyncAction.Loading val isDescriptionInError = sending is AsyncAction.Failure && diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt index 2c8dba4af3..9c1c25e153 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt @@ -84,7 +84,7 @@ fun BugReportView( supportingText = stringResource(id = R.string.screen_bug_report_editor_description), onValueChange = { descriptionFieldState = it - eventSink(BugReportEvents.SetDescription(it)) + eventSink(BugReportEvent.SetDescription(it)) }, keyboardOptions = KeyboardOptions( capitalization = KeyboardCapitalization.Sentences, @@ -110,14 +110,14 @@ fun BugReportView( PreferenceDivider() PreferenceSwitch( isChecked = state.formState.sendLogs, - onCheckedChange = { eventSink(BugReportEvents.SetSendLog(it)) }, + onCheckedChange = { eventSink(BugReportEvent.SetSendLog(it)) }, enabled = isFormEnabled, title = stringResource(id = R.string.screen_bug_report_include_logs), subtitle = stringResource(id = R.string.screen_bug_report_logs_description), ) PreferenceSwitch( isChecked = state.formState.canContact, - onCheckedChange = { eventSink(BugReportEvents.SetCanContact(it)) }, + onCheckedChange = { eventSink(BugReportEvent.SetCanContact(it)) }, enabled = isFormEnabled, title = stringResource(id = R.string.screen_bug_report_contact_me_title), subtitle = stringResource(id = R.string.screen_bug_report_contact_me), @@ -125,7 +125,7 @@ fun BugReportView( if (state.screenshotUri != null) { PreferenceSwitch( isChecked = state.formState.sendScreenshot, - onCheckedChange = { eventSink(BugReportEvents.SetSendScreenshot(it)) }, + onCheckedChange = { eventSink(BugReportEvent.SetSendScreenshot(it)) }, enabled = isFormEnabled, title = stringResource(id = R.string.screen_bug_report_include_screenshot) ) @@ -151,7 +151,7 @@ fun BugReportView( } PreferenceSwitch( isChecked = state.formState.sendPushRules, - onCheckedChange = { eventSink(BugReportEvents.SetSendPushRules(it)) }, + onCheckedChange = { eventSink(BugReportEvent.SetSendPushRules(it)) }, enabled = isFormEnabled, title = stringResource(R.string.screen_bug_report_send_notification_settings_title), subtitle = stringResource(R.string.screen_bug_report_send_notification_settings_description), @@ -179,12 +179,12 @@ fun BugReportView( onValueChange = { if (it.isEmpty()) { ghIssueNumberState = "" - eventSink(BugReportEvents.SetGhIssueNumber(null)) + eventSink(BugReportEvent.SetGhIssueNumber(null)) } else { val number = it.toIntOrNull()?.takeIf { ghInt -> ghInt in 1..99_999 } number?.let { ghIssueNumber -> ghIssueNumberState = ghIssueNumber.toString() - eventSink(BugReportEvents.SetGhIssueNumber(ghIssueNumber)) + eventSink(BugReportEvent.SetGhIssueNumber(ghIssueNumber)) } } }, @@ -202,7 +202,7 @@ fun BugReportView( PreferenceRow { Button( text = stringResource(id = CommonStrings.action_send), - onClick = { eventSink(BugReportEvents.SendBugReport) }, + onClick = { eventSink(BugReportEvent.SendBugReport) }, enabled = state.submitEnabled, showProgress = state.sending.isLoading(), modifier = Modifier @@ -216,7 +216,7 @@ fun BugReportView( async = state.sending, progressDialog = { }, onSuccess = { - eventSink(BugReportEvents.ResetAll) + eventSink(BugReportEvent.ResetAll) onSuccess() }, errorMessage = { error -> @@ -225,7 +225,7 @@ fun BugReportView( else -> error.message ?: error.toString() } }, - onErrorDismiss = { eventSink(BugReportEvents.ClearError) }, + onErrorDismiss = { eventSink(BugReportEvent.ClearError) }, ) } } 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 afbcc939a6..904ff881f0 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 @@ -51,9 +51,9 @@ class BugReportPresenterTest { val presenter = createPresenter() presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(BugReportEvents.SetDescription(A_SHORT_DESCRIPTION)) + initialState.eventSink.invoke(BugReportEvent.SetDescription(A_SHORT_DESCRIPTION)) assertThat(awaitItem().submitEnabled).isTrue() - initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION)) + initialState.eventSink.invoke(BugReportEvent.SetDescription(A_LONG_DESCRIPTION)) assertThat(awaitItem().submitEnabled).isTrue() } } @@ -63,9 +63,9 @@ class BugReportPresenterTest { val presenter = createPresenter() presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(BugReportEvents.SetCanContact(true)) + initialState.eventSink.invoke(BugReportEvent.SetCanContact(true)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(canContact = true)) - initialState.eventSink.invoke(BugReportEvents.SetCanContact(false)) + initialState.eventSink.invoke(BugReportEvent.SetCanContact(false)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(canContact = false)) } } @@ -76,9 +76,9 @@ class BugReportPresenterTest { presenter.test { val initialState = awaitItem() // Since this is true by default, start by disabling - initialState.eventSink.invoke(BugReportEvents.SetSendLog(false)) + initialState.eventSink.invoke(BugReportEvent.SetSendLog(false)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendLogs = false)) - initialState.eventSink.invoke(BugReportEvents.SetSendLog(true)) + initialState.eventSink.invoke(BugReportEvent.SetSendLog(true)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendLogs = true)) } } @@ -88,9 +88,9 @@ class BugReportPresenterTest { val presenter = createPresenter() presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(BugReportEvents.SetSendScreenshot(true)) + initialState.eventSink.invoke(BugReportEvent.SetSendScreenshot(true)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendScreenshot = true)) - initialState.eventSink.invoke(BugReportEvents.SetSendScreenshot(false)) + initialState.eventSink.invoke(BugReportEvent.SetSendScreenshot(false)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendScreenshot = false)) } } @@ -100,9 +100,9 @@ class BugReportPresenterTest { val presenter = createPresenter() presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(BugReportEvents.SetSendPushRules(true)) + initialState.eventSink.invoke(BugReportEvent.SetSendPushRules(true)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendPushRules = true)) - initialState.eventSink.invoke(BugReportEvents.SetSendPushRules(false)) + initialState.eventSink.invoke(BugReportEvent.SetSendPushRules(false)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendPushRules = false)) } } @@ -112,9 +112,9 @@ class BugReportPresenterTest { val presenter = createPresenter() presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(BugReportEvents.SetGhIssueNumber(1)) + initialState.eventSink.invoke(BugReportEvent.SetGhIssueNumber(1)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(ghIssueNumber = 1)) - initialState.eventSink.invoke(BugReportEvents.SetGhIssueNumber(null)) + initialState.eventSink.invoke(BugReportEvent.SetGhIssueNumber(null)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(ghIssueNumber = null)) } } @@ -130,7 +130,7 @@ class BugReportPresenterTest { val initialState = awaitItem() assertThat(initialState.hasCrashLogs).isTrue() assertThat(initialState.screenshotUri).isEqualTo(A_SCREENSHOT_URI) - initialState.eventSink.invoke(BugReportEvents.ResetAll) + initialState.eventSink.invoke(BugReportEvent.ResetAll) val resetState = awaitItem() assertThat(resetState.hasCrashLogs).isFalse() // TODO Make it live assertThat(resetState.screenshotUri).isNull() @@ -146,9 +146,9 @@ class BugReportPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION)) + initialState.eventSink.invoke(BugReportEvent.SetDescription(A_LONG_DESCRIPTION)) skipItems(1) - initialState.eventSink.invoke(BugReportEvents.SendBugReport) + initialState.eventSink.invoke(BugReportEvent.SendBugReport) skipItems(1) val progressState = awaitItem() assertThat(progressState.sending).isEqualTo(AsyncAction.Loading) @@ -170,9 +170,9 @@ class BugReportPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION)) + initialState.eventSink.invoke(BugReportEvent.SetDescription(A_LONG_DESCRIPTION)) skipItems(1) - initialState.eventSink.invoke(BugReportEvents.SendBugReport) + initialState.eventSink.invoke(BugReportEvent.SendBugReport) skipItems(1) val progressState = awaitItem() assertThat(progressState.sending).isEqualTo(AsyncAction.Loading) @@ -182,7 +182,7 @@ class BugReportPresenterTest { assertThat(awaitItem().sendingProgress).isEqualTo(0f) assertThat((awaitItem().sending as AsyncAction.Failure).error.message).isEqualTo(A_FAILURE_REASON) // Reset failure - initialState.eventSink.invoke(BugReportEvents.ClearError) + initialState.eventSink.invoke(BugReportEvent.ClearError) val lastItem = awaitItem() assertThat(lastItem.sendingProgress).isEqualTo(0f) assertThat(lastItem.sending).isInstanceOf(AsyncAction.Uninitialized::class.java) @@ -194,13 +194,13 @@ class BugReportPresenterTest { val presenter = createPresenter() presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(BugReportEvents.SetDescription(A_SHORT_DESCRIPTION)) + initialState.eventSink.invoke(BugReportEvent.SetDescription(A_SHORT_DESCRIPTION)) skipItems(1) - initialState.eventSink.invoke(BugReportEvents.SendBugReport) + initialState.eventSink.invoke(BugReportEvent.SendBugReport) val errorState = awaitItem() assertThat(errorState.sending).isEqualTo(AsyncAction.Failure(BugReportFormError.DescriptionTooShort)) // Reset failure - initialState.eventSink.invoke(BugReportEvents.ClearError) + initialState.eventSink.invoke(BugReportEvent.ClearError) val lastItem = awaitItem() assertThat(lastItem.sending).isInstanceOf(AsyncAction.Uninitialized::class.java) } @@ -215,9 +215,9 @@ class BugReportPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION)) + initialState.eventSink.invoke(BugReportEvent.SetDescription(A_LONG_DESCRIPTION)) skipItems(1) - initialState.eventSink.invoke(BugReportEvents.SendBugReport) + initialState.eventSink.invoke(BugReportEvent.SendBugReport) skipItems(1) val progressState = awaitItem() assertThat(progressState.sending).isEqualTo(AsyncAction.Loading) From fc8d3b3065753e76ead82b0f3d9aabf8a95646d0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:14:32 +0200 Subject: [PATCH 046/112] `ReportRoomEvents` -> `ReportRoomEvent` --- .../{ReportRoomEvents.kt => ReportRoomEvent.kt} | 10 +++++----- .../reportroom/impl/ReportRoomPresenter.kt | 10 +++++----- .../features/reportroom/impl/ReportRoomState.kt | 2 +- .../impl/ReportRoomStatePreviewParam.kt | 2 +- .../features/reportroom/impl/ReportRoomView.kt | 10 +++++----- .../reportroom/impl/ReportRoomPresenterTest.kt | 16 ++++++++-------- .../reportroom/impl/ReportRoomViewTest.kt | 16 ++++++++-------- 7 files changed, 33 insertions(+), 33 deletions(-) rename features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/{ReportRoomEvents.kt => ReportRoomEvent.kt} (65%) diff --git a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomEvents.kt b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomEvent.kt similarity index 65% rename from features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomEvents.kt rename to features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomEvent.kt index f7185f7fcd..e4df50fa7c 100644 --- a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomEvents.kt +++ b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomEvent.kt @@ -8,9 +8,9 @@ package io.element.android.features.reportroom.impl -sealed interface ReportRoomEvents { - data class UpdateReason(val reason: String) : ReportRoomEvents - data object ToggleLeaveRoom : ReportRoomEvents - data object Report : ReportRoomEvents - data object ClearReportAction : ReportRoomEvents +sealed interface ReportRoomEvent { + data class UpdateReason(val reason: String) : ReportRoomEvent + data object ToggleLeaveRoom : ReportRoomEvent + data object Report : ReportRoomEvent + data object ClearReportAction : ReportRoomEvent } diff --git a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomPresenter.kt b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomPresenter.kt index 422cf42718..25f9b56d59 100644 --- a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomPresenter.kt +++ b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomPresenter.kt @@ -44,16 +44,16 @@ class ReportRoomPresenter( val coroutineScope = rememberCoroutineScope() - fun handleEvent(event: ReportRoomEvents) { + fun handleEvent(event: ReportRoomEvent) { when (event) { - ReportRoomEvents.Report -> coroutineScope.reportRoom(reason, leaveRoom, reportAction) - ReportRoomEvents.ToggleLeaveRoom -> { + ReportRoomEvent.Report -> coroutineScope.reportRoom(reason, leaveRoom, reportAction) + ReportRoomEvent.ToggleLeaveRoom -> { leaveRoom = !leaveRoom } - is ReportRoomEvents.UpdateReason -> { + is ReportRoomEvent.UpdateReason -> { reason = event.reason } - ReportRoomEvents.ClearReportAction -> { + ReportRoomEvent.ClearReportAction -> { reportAction.value = AsyncAction.Uninitialized } } diff --git a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomState.kt b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomState.kt index fa09306991..28427705b0 100644 --- a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomState.kt +++ b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomState.kt @@ -14,7 +14,7 @@ data class ReportRoomState( val reason: String, val leaveRoom: Boolean, val reportAction: AsyncAction, - val eventSink: (ReportRoomEvents) -> Unit + val eventSink: (ReportRoomEvent) -> Unit ) { val canReport: Boolean = reason.isNotBlank() } diff --git a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomStatePreviewParam.kt b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomStatePreviewParam.kt index 11d2777446..421c55f0e3 100644 --- a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomStatePreviewParam.kt +++ b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomStatePreviewParam.kt @@ -30,7 +30,7 @@ fun aReportRoomState( reason: String = "", leaveRoom: Boolean = false, reportAction: AsyncAction = AsyncAction.Uninitialized, - eventSink: (ReportRoomEvents) -> Unit = {} + eventSink: (ReportRoomEvent) -> Unit = {} ) = ReportRoomState( reason = reason, leaveRoom = leaveRoom, diff --git a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomView.kt b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomView.kt index 7b614b13cb..df67b44b22 100644 --- a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomView.kt +++ b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomView.kt @@ -66,9 +66,9 @@ fun ReportRoomView( } }, onRetry = { - state.eventSink(ReportRoomEvents.Report) + state.eventSink(ReportRoomEvent.Report) }, - onErrorDismiss = { state.eventSink(ReportRoomEvents.ClearReportAction) } + onErrorDismiss = { state.eventSink(ReportRoomEvent.ClearReportAction) } ) Scaffold( @@ -93,7 +93,7 @@ fun ReportRoomView( ) { TextField( value = state.reason, - onValueChange = { state.eventSink(ReportRoomEvents.UpdateReason(it)) }, + onValueChange = { state.eventSink(ReportRoomEvent.UpdateReason(it)) }, placeholder = stringResource(R.string.screen_report_room_reason_placeholder), minLines = 3, enabled = !isReporting, @@ -112,7 +112,7 @@ fun ReportRoomView( Text(text = stringResource(CommonStrings.action_leave_room)) }, onClick = { - state.eventSink(ReportRoomEvents.ToggleLeaveRoom) + state.eventSink(ReportRoomEvent.ToggleLeaveRoom) }, trailingContent = ListItemContent.Switch(checked = state.leaveRoom) ) @@ -126,7 +126,7 @@ fun ReportRoomView( showProgress = isReporting, onClick = { focusManager.clearFocus(force = true) - state.eventSink(ReportRoomEvents.Report) + state.eventSink(ReportRoomEvent.Report) }, modifier = Modifier .fillMaxWidth() diff --git a/features/reportroom/impl/src/test/kotlin/io/element/android/features/reportroom/impl/ReportRoomPresenterTest.kt b/features/reportroom/impl/src/test/kotlin/io/element/android/features/reportroom/impl/ReportRoomPresenterTest.kt index 6bcaeac02d..87b3c49b47 100644 --- a/features/reportroom/impl/src/test/kotlin/io/element/android/features/reportroom/impl/ReportRoomPresenterTest.kt +++ b/features/reportroom/impl/src/test/kotlin/io/element/android/features/reportroom/impl/ReportRoomPresenterTest.kt @@ -43,13 +43,13 @@ class ReportRoomPresenterTest { assertThat(state.reason).isEmpty() assertThat(state.canReport).isFalse() assertThat(state.leaveRoom).isFalse() - state.eventSink(ReportRoomEvents.UpdateReason("Spam")) + state.eventSink(ReportRoomEvent.UpdateReason("Spam")) } awaitItem().also { state -> assertThat(state.reason).isEqualTo("Spam") assertThat(state.canReport).isTrue() assertThat(state.leaveRoom).isFalse() - state.eventSink(ReportRoomEvents.ToggleLeaveRoom) + state.eventSink(ReportRoomEvent.ToggleLeaveRoom) } awaitItem().also { state -> assertThat(state.leaveRoom).isTrue() @@ -68,8 +68,8 @@ class ReportRoomPresenterTest { ) val presenter = createReportRoomPresenter(roomId = roomId, reportRoom = reportRoom) presenter.test { - awaitItem().eventSink(ReportRoomEvents.ToggleLeaveRoom) - awaitItem().eventSink(ReportRoomEvents.Report) + awaitItem().eventSink(ReportRoomEvent.ToggleLeaveRoom) + awaitItem().eventSink(ReportRoomEvent.Report) awaitItem().also { state -> assertThat(state.reportAction).isInstanceOf(AsyncAction.Loading::class.java) } @@ -93,7 +93,7 @@ class ReportRoomPresenterTest { ) val presenter = createReportRoomPresenter(roomId = roomId, reportRoom = reportRoom) presenter.test { - awaitItem().eventSink(ReportRoomEvents.Report) + awaitItem().eventSink(ReportRoomEvent.Report) awaitItem().also { state -> assertThat(state.reportAction).isInstanceOf(AsyncAction.Loading::class.java) } @@ -117,14 +117,14 @@ class ReportRoomPresenterTest { ) val presenter = createReportRoomPresenter(roomId = roomId, reportRoom = reportRoom) presenter.test { - awaitItem().eventSink(ReportRoomEvents.ToggleLeaveRoom) - awaitItem().eventSink(ReportRoomEvents.Report) + awaitItem().eventSink(ReportRoomEvent.ToggleLeaveRoom) + awaitItem().eventSink(ReportRoomEvent.Report) awaitItem().also { state -> assertThat(state.reportAction).isInstanceOf(AsyncAction.Loading::class.java) } awaitItem().also { state -> assertThat(state.reportAction).isInstanceOf(AsyncAction.Failure::class.java) - state.eventSink(ReportRoomEvents.Report) + state.eventSink(ReportRoomEvent.Report) } awaitItem().also { state -> assertThat(state.reportAction).isInstanceOf(AsyncAction.Loading::class.java) diff --git a/features/reportroom/impl/src/test/kotlin/io/element/android/features/reportroom/impl/ReportRoomViewTest.kt b/features/reportroom/impl/src/test/kotlin/io/element/android/features/reportroom/impl/ReportRoomViewTest.kt index 0e43cf6594..64c8744424 100644 --- a/features/reportroom/impl/src/test/kotlin/io/element/android/features/reportroom/impl/ReportRoomViewTest.kt +++ b/features/reportroom/impl/src/test/kotlin/io/element/android/features/reportroom/impl/ReportRoomViewTest.kt @@ -28,7 +28,7 @@ import org.junit.Test class ReportRoomViewTest : RobolectricTest() { @Test fun `clicking on back invoke the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setReportRoomView( aReportRoomState( @@ -42,7 +42,7 @@ class ReportRoomViewTest : RobolectricTest() { @Test fun `clicking on report when enabled emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setReportRoomView( aReportRoomState( reason = "Spam", @@ -50,12 +50,12 @@ class ReportRoomViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_report) - eventsRecorder.assertSingle(ReportRoomEvents.Report) + eventsRecorder.assertSingle(ReportRoomEvent.Report) } @Test fun `clicking on decline when disabled does not emit event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) setReportRoomView( aReportRoomState(eventSink = eventsRecorder), ) @@ -64,17 +64,17 @@ class ReportRoomViewTest : RobolectricTest() { @Test fun `clicking on leave room option emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setReportRoomView( aReportRoomState(eventSink = eventsRecorder), ) clickOn(CommonStrings.action_leave_room) - eventsRecorder.assertSingle(ReportRoomEvents.ToggleLeaveRoom) + eventsRecorder.assertSingle(ReportRoomEvent.ToggleLeaveRoom) } @Test fun `typing text in the reason field emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setReportRoomView( aReportRoomState( eventSink = eventsRecorder, @@ -82,7 +82,7 @@ class ReportRoomViewTest : RobolectricTest() { ), ) onNodeWithText("").performTextInput("Spam!") - eventsRecorder.assertSingle(ReportRoomEvents.UpdateReason("Spam!")) + eventsRecorder.assertSingle(ReportRoomEvent.UpdateReason("Spam!")) } } From 4e2d99c96860e44da018c34bada35f5cbc4084f1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:15:35 +0200 Subject: [PATCH 047/112] `RolesAndPermissionsEvents` -> `RolesAndPermissionsEvent` --- ...sEvents.kt => RolesAndPermissionsEvent.kt} | 10 ++++---- .../impl/root/RolesAndPermissionsPresenter.kt | 10 ++++---- .../impl/root/RolesAndPermissionsState.kt | 2 +- .../RolesAndPermissionsStatePreviewParam.kt | 2 +- .../impl/root/RolesAndPermissionsView.kt | 20 ++++++++-------- .../root/RolesAndPermissionPresenterTest.kt | 20 ++++++++-------- .../impl/root/RolesAndPermissionsViewTest.kt | 24 +++++++++---------- 7 files changed, 44 insertions(+), 44 deletions(-) rename features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/{RolesAndPermissionsEvents.kt => RolesAndPermissionsEvent.kt} (63%) diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsEvents.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsEvent.kt similarity index 63% rename from features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsEvents.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsEvent.kt index 69fad02c25..b250fee2d1 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsEvents.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsEvent.kt @@ -10,9 +10,9 @@ package io.element.android.features.rolesandpermissions.impl.root import io.element.android.libraries.matrix.api.room.RoomMember -sealed interface RolesAndPermissionsEvents { - data object ChangeOwnRole : RolesAndPermissionsEvents - data class DemoteSelfTo(val role: RoomMember.Role) : RolesAndPermissionsEvents - data object ResetPermissions : RolesAndPermissionsEvents - data object CancelPendingAction : RolesAndPermissionsEvents +sealed interface RolesAndPermissionsEvent { + data object ChangeOwnRole : RolesAndPermissionsEvent + data class DemoteSelfTo(val role: RoomMember.Role) : RolesAndPermissionsEvent + data object ResetPermissions : RolesAndPermissionsEvent + data object CancelPendingAction : RolesAndPermissionsEvent } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsPresenter.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsPresenter.kt index dd3a59b99e..1231089af0 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsPresenter.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsPresenter.kt @@ -63,20 +63,20 @@ class RolesAndPermissionsPresenter( val changeOwnRoleAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } val resetPermissionsAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } - fun handleEvent(event: RolesAndPermissionsEvents) { + fun handleEvent(event: RolesAndPermissionsEvent) { when (event) { - is RolesAndPermissionsEvents.ChangeOwnRole -> { + is RolesAndPermissionsEvent.ChangeOwnRole -> { changeOwnRoleAction.value = AsyncAction.ConfirmingNoParams } - is RolesAndPermissionsEvents.CancelPendingAction -> { + is RolesAndPermissionsEvent.CancelPendingAction -> { changeOwnRoleAction.value = AsyncAction.Uninitialized resetPermissionsAction.value = AsyncAction.Uninitialized } - is RolesAndPermissionsEvents.DemoteSelfTo -> coroutineScope.demoteSelfTo( + is RolesAndPermissionsEvent.DemoteSelfTo -> coroutineScope.demoteSelfTo( role = event.role, changeOwnRoleAction = changeOwnRoleAction, ) - is RolesAndPermissionsEvents.ResetPermissions -> if (resetPermissionsAction.value.isConfirming()) { + is RolesAndPermissionsEvent.ResetPermissions -> if (resetPermissionsAction.value.isConfirming()) { coroutineScope.resetPermissions(resetPermissionsAction) } else { resetPermissionsAction.value = AsyncAction.ConfirmingNoParams diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsState.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsState.kt index 626ad3b699..a1345b27c2 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsState.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsState.kt @@ -20,7 +20,7 @@ data class RolesAndPermissionsState( val availableSelfDemoteActions: ImmutableList, val changeOwnRoleAction: AsyncAction, val resetPermissionsAction: AsyncAction, - val eventSink: (RolesAndPermissionsEvents) -> Unit, + val eventSink: (RolesAndPermissionsEvent) -> Unit, ) { val canSelfDemote = availableSelfDemoteActions.isNotEmpty() } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStatePreviewParam.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStatePreviewParam.kt index 2fc39212d8..2229c185b9 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStatePreviewParam.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStatePreviewParam.kt @@ -58,7 +58,7 @@ internal fun aRolesAndPermissionsState( availableSelfDemoteActions: List = listOf(SelfDemoteAction.ToModerator, SelfDemoteAction.ToMember), changeOwnRoleAction: AsyncAction = AsyncAction.Uninitialized, resetPermissionsAction: AsyncAction = AsyncAction.Uninitialized, - eventSink: (RolesAndPermissionsEvents) -> Unit = {}, + eventSink: (RolesAndPermissionsEvent) -> Unit = {}, ) = RolesAndPermissionsState( roomSupportsOwnerRole = roomSupportsOwners, adminCount = adminCount, diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt index d9c09bbc64..6c9c4396b2 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt @@ -80,7 +80,7 @@ fun RolesAndPermissionsView( if (state.canSelfDemote) { ListItem( content = { Text(stringResource(R.string.screen_room_roles_and_permissions_change_my_role)) }, - onClick = { state.eventSink(RolesAndPermissionsEvents.ChangeOwnRole) }, + onClick = { state.eventSink(RolesAndPermissionsEvent.ChangeOwnRole) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Edit())) ) } @@ -94,7 +94,7 @@ fun RolesAndPermissionsView( ListItem( content = { Text(stringResource(R.string.screen_room_roles_and_permissions_reset)) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Delete())), - onClick = { state.eventSink(RolesAndPermissionsEvents.ResetPermissions) }, + onClick = { state.eventSink(RolesAndPermissionsEvent.ResetPermissions) }, style = ListItemStyle.Destructive, ) } @@ -107,12 +107,12 @@ fun RolesAndPermissionsView( content = stringResource(R.string.screen_room_roles_and_permissions_reset_confirm_description), submitText = stringResource(CommonStrings.action_reset), destructiveSubmit = true, - onSubmitClick = { state.eventSink(RolesAndPermissionsEvents.ResetPermissions) }, - onDismiss = { state.eventSink(RolesAndPermissionsEvents.CancelPendingAction) }, + onSubmitClick = { state.eventSink(RolesAndPermissionsEvent.ResetPermissions) }, + onDismiss = { state.eventSink(RolesAndPermissionsEvent.CancelPendingAction) }, ) }, - onSuccess = { state.eventSink(RolesAndPermissionsEvents.CancelPendingAction) }, - onErrorDismiss = { state.eventSink(RolesAndPermissionsEvents.CancelPendingAction) } + onSuccess = { state.eventSink(RolesAndPermissionsEvent.CancelPendingAction) }, + onErrorDismiss = { state.eventSink(RolesAndPermissionsEvent.CancelPendingAction) } ) when (state.changeOwnRoleAction) { @@ -128,7 +128,7 @@ fun RolesAndPermissionsView( is AsyncAction.Failure -> { ErrorDialog( content = stringResource(CommonStrings.error_unknown), - onSubmit = { state.eventSink(RolesAndPermissionsEvents.CancelPendingAction) } + onSubmit = { state.eventSink(RolesAndPermissionsEvent.CancelPendingAction) } ) } else -> Unit @@ -139,7 +139,7 @@ fun RolesAndPermissionsView( @Composable private fun ChangeOwnRoleBottomSheet( availableDemoteActions: ImmutableList, - eventSink: (RolesAndPermissionsEvents) -> Unit, + eventSink: (RolesAndPermissionsEvent) -> Unit, ) { val coroutineScope = rememberCoroutineScope() val sheetState = rememberBottomSheetState( @@ -148,7 +148,7 @@ private fun ChangeOwnRoleBottomSheet( ) fun dismiss() { sheetState.hide(coroutineScope) { - eventSink(RolesAndPermissionsEvents.CancelPendingAction) + eventSink(RolesAndPermissionsEvent.CancelPendingAction) } } ModalBottomSheet( @@ -176,7 +176,7 @@ private fun ChangeOwnRoleBottomSheet( content = { Text(stringResource(demoteAction.titleRes)) }, onClick = { sheetState.hide(coroutineScope) { - eventSink(RolesAndPermissionsEvents.DemoteSelfTo(demoteAction.role)) + eventSink(RolesAndPermissionsEvent.DemoteSelfTo(demoteAction.role)) } }, style = ListItemStyle.Destructive, diff --git a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionPresenterTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionPresenterTest.kt index 54bd6b7987..29ba0d368a 100644 --- a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionPresenterTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionPresenterTest.kt @@ -45,7 +45,7 @@ class RolesAndPermissionPresenterTest { val presenter = createRolesAndPermissionsPresenter() presenter.test { val initialState = awaitItem() - initialState.eventSink(RolesAndPermissionsEvents.ChangeOwnRole) + initialState.eventSink(RolesAndPermissionsEvent.ChangeOwnRole) assertThat(awaitItem().changeOwnRoleAction).isEqualTo(AsyncAction.ConfirmingNoParams) } } @@ -62,7 +62,7 @@ class RolesAndPermissionPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.Moderator)) + initialState.eventSink(RolesAndPermissionsEvent.DemoteSelfTo(RoomMember.Role.Moderator)) runCurrent() assertThat(awaitItem().changeOwnRoleAction).isEqualTo(AsyncAction.Loading) @@ -82,7 +82,7 @@ class RolesAndPermissionPresenterTest { val presenter = createRolesAndPermissionsPresenter(room = room, dispatchers = testCoroutineDispatchers()) presenter.test { val initialState = awaitItem() - initialState.eventSink(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.Moderator)) + initialState.eventSink(RolesAndPermissionsEvent.DemoteSelfTo(RoomMember.Role.Moderator)) runCurrent() assertThat(awaitItem().changeOwnRoleAction).isEqualTo(AsyncAction.Loading) @@ -90,7 +90,7 @@ class RolesAndPermissionPresenterTest { runCurrent() assertThat(awaitItem().changeOwnRoleAction).isInstanceOf(AsyncAction.Failure::class.java) - initialState.eventSink(RolesAndPermissionsEvents.CancelPendingAction) + initialState.eventSink(RolesAndPermissionsEvent.CancelPendingAction) assertThat(awaitItem().changeOwnRoleAction).isEqualTo(AsyncAction.Uninitialized) } } @@ -100,8 +100,8 @@ class RolesAndPermissionPresenterTest { val presenter = createRolesAndPermissionsPresenter() presenter.test { val initialState = awaitItem() - initialState.eventSink(RolesAndPermissionsEvents.ChangeOwnRole) - awaitItem().eventSink(RolesAndPermissionsEvents.CancelPendingAction) + initialState.eventSink(RolesAndPermissionsEvent.ChangeOwnRole) + awaitItem().eventSink(RolesAndPermissionsEvent.CancelPendingAction) assertThat(awaitItem().changeOwnRoleAction).isEqualTo(AsyncAction.Uninitialized) } @@ -119,9 +119,9 @@ class RolesAndPermissionPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(RolesAndPermissionsEvents.ResetPermissions) + initialState.eventSink(RolesAndPermissionsEvent.ResetPermissions) // Confirmation - awaitItem().eventSink(RolesAndPermissionsEvents.ResetPermissions) + awaitItem().eventSink(RolesAndPermissionsEvent.ResetPermissions) assertThat(awaitItem().resetPermissionsAction).isEqualTo(AsyncAction.Loading) assertThat(awaitItem().resetPermissionsAction).isEqualTo(AsyncAction.Success(Unit)) @@ -134,8 +134,8 @@ class RolesAndPermissionPresenterTest { val presenter = createRolesAndPermissionsPresenter() presenter.test { val initialState = awaitItem() - initialState.eventSink(RolesAndPermissionsEvents.ResetPermissions) - awaitItem().eventSink(RolesAndPermissionsEvents.CancelPendingAction) + initialState.eventSink(RolesAndPermissionsEvent.ResetPermissions) + awaitItem().eventSink(RolesAndPermissionsEvent.CancelPendingAction) assertThat(awaitItem().resetPermissionsAction).isEqualTo(AsyncAction.Uninitialized) } diff --git a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsViewTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsViewTest.kt index 8a3b6fd766..7e94468d48 100644 --- a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsViewTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsViewTest.kt @@ -92,19 +92,19 @@ class RolesAndPermissionsViewTest : RobolectricTest() { @Test @Config(qualifiers = "h640dp") fun `tapping on reset permissions triggers ResetPermissions event`() = runAndroidComposeUiTest { - val recorder = EventsRecorder() + val recorder = EventsRecorder() setRolesAndPermissionsView( state = aRolesAndPermissionsState( eventSink = recorder, ), ) clickOn(R.string.screen_room_roles_and_permissions_reset) - recorder.assertSingle(RolesAndPermissionsEvents.ResetPermissions) + recorder.assertSingle(RolesAndPermissionsEvent.ResetPermissions) } @Test fun `tapping on Reset in the reset permissions confirmation dialog triggers ResetPermissions event`() = runAndroidComposeUiTest { - val recorder = EventsRecorder() + val recorder = EventsRecorder() setRolesAndPermissionsView( state = aRolesAndPermissionsState( resetPermissionsAction = AsyncAction.ConfirmingNoParams, @@ -112,12 +112,12 @@ class RolesAndPermissionsViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_reset) - recorder.assertSingle(RolesAndPermissionsEvents.ResetPermissions) + recorder.assertSingle(RolesAndPermissionsEvent.ResetPermissions) } @Test fun `tapping on Cancel in the reset permissions confirmation dialog triggers CancelPendingAction event`() = runAndroidComposeUiTest { - val recorder = EventsRecorder() + val recorder = EventsRecorder() setRolesAndPermissionsView( state = aRolesAndPermissionsState( resetPermissionsAction = AsyncAction.ConfirmingNoParams, @@ -125,12 +125,12 @@ class RolesAndPermissionsViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_cancel) - recorder.assertSingle(RolesAndPermissionsEvents.CancelPendingAction) + recorder.assertSingle(RolesAndPermissionsEvent.CancelPendingAction) } @Test fun `tapping on 'Demote to moderator' in the demote self bottom sheet triggers the right event`() = runAndroidComposeUiTest { - val recorder = EventsRecorder() + val recorder = EventsRecorder() setRolesAndPermissionsView( state = aRolesAndPermissionsState( changeOwnRoleAction = AsyncAction.ConfirmingNoParams, @@ -139,12 +139,12 @@ class RolesAndPermissionsViewTest : RobolectricTest() { ) clickOn(R.string.screen_room_roles_and_permissions_change_role_demote_to_moderator) mainClock.advanceTimeBy(1_000L) - recorder.assertSingle(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.Moderator)) + recorder.assertSingle(RolesAndPermissionsEvent.DemoteSelfTo(RoomMember.Role.Moderator)) } @Test fun `tapping on 'Demote to member' in the demote self bottom sheet triggers the right event`() = runAndroidComposeUiTest { - val recorder = EventsRecorder() + val recorder = EventsRecorder() setRolesAndPermissionsView( state = aRolesAndPermissionsState( changeOwnRoleAction = AsyncAction.ConfirmingNoParams, @@ -153,12 +153,12 @@ class RolesAndPermissionsViewTest : RobolectricTest() { ) clickOn(R.string.screen_room_roles_and_permissions_change_role_demote_to_member) mainClock.advanceTimeBy(1_000L) - recorder.assertSingle(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.User)) + recorder.assertSingle(RolesAndPermissionsEvent.DemoteSelfTo(RoomMember.Role.User)) } @Test fun `tapping on 'Cancel' in the demote self bottom sheet triggers the right event`() = runAndroidComposeUiTest { - val recorder = EventsRecorder() + val recorder = EventsRecorder() setRolesAndPermissionsView( state = aRolesAndPermissionsState( changeOwnRoleAction = AsyncAction.ConfirmingNoParams, @@ -167,7 +167,7 @@ class RolesAndPermissionsViewTest : RobolectricTest() { ) clickOn(CommonStrings.action_cancel) mainClock.advanceTimeBy(1_000L) - recorder.assertSingle(RolesAndPermissionsEvents.CancelPendingAction) + recorder.assertSingle(RolesAndPermissionsEvent.CancelPendingAction) } } From 15569ef3e8897bf9555972c7dd7fe2773f1010fe Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:16:32 +0200 Subject: [PATCH 048/112] `RoomAliasResolverEvents` -> `RoomAliasResolverEvent` --- ...omAliasResolverEvents.kt => RoomAliasResolverEvent.kt} | 6 +++--- .../roomaliasresolver/impl/RoomAliasResolverPresenter.kt | 6 +++--- .../roomaliasresolver/impl/RoomAliasResolverState.kt | 2 +- .../impl/RoomAliasResolverStatePreviewParam.kt | 2 +- .../roomaliasresolver/impl/RoomAliasResolverView.kt | 4 ++-- .../impl/RoomAliasHelperPresenterTest.kt | 2 +- .../roomaliasresolver/impl/RoomAliasHelperViewTest.kt | 8 ++++---- 7 files changed, 15 insertions(+), 15 deletions(-) rename features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/{RoomAliasResolverEvents.kt => RoomAliasResolverEvent.kt} (67%) diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverEvents.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverEvent.kt similarity index 67% rename from features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverEvents.kt rename to features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverEvent.kt index 11b92ba244..6c528cea14 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverEvents.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverEvent.kt @@ -8,7 +8,7 @@ package io.element.android.features.roomaliasresolver.impl -sealed interface RoomAliasResolverEvents { - data object Retry : RoomAliasResolverEvents - data object DismissError : RoomAliasResolverEvents +sealed interface RoomAliasResolverEvent { + data object Retry : RoomAliasResolverEvent + data object DismissError : RoomAliasResolverEvent } diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt index 1ec0ce0691..41a49d0fe1 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt @@ -48,10 +48,10 @@ class RoomAliasResolverPresenter( resolveAlias(resolveState) } - fun handleEvent(event: RoomAliasResolverEvents) { + fun handleEvent(event: RoomAliasResolverEvent) { when (event) { - RoomAliasResolverEvents.Retry -> coroutineScope.resolveAlias(resolveState) - RoomAliasResolverEvents.DismissError -> resolveState.value = AsyncData.Uninitialized + RoomAliasResolverEvent.Retry -> coroutineScope.resolveAlias(resolveState) + RoomAliasResolverEvent.DismissError -> resolveState.value = AsyncData.Uninitialized } } diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverState.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverState.kt index 82d13f16a5..1974fdff7c 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverState.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverState.kt @@ -15,7 +15,7 @@ import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias data class RoomAliasResolverState( val roomAlias: RoomAlias, val resolveState: AsyncData, - val eventSink: (RoomAliasResolverEvents) -> Unit + val eventSink: (RoomAliasResolverEvent) -> Unit ) sealed class RoomAliasResolverFailures : Exception() { diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverStatePreviewParam.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverStatePreviewParam.kt index 6f93930b99..1bb4e03a45 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverStatePreviewParam.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverStatePreviewParam.kt @@ -30,7 +30,7 @@ open class RoomAliasResolverStatePreviewParam : PreviewParameterProvider = AsyncData.Uninitialized, - eventSink: (RoomAliasResolverEvents) -> Unit = {} + eventSink: (RoomAliasResolverEvent) -> Unit = {} ) = RoomAliasResolverState( roomAlias = roomAlias, resolveState = resolveState, diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt index 37960a50e2..dd002814b1 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt @@ -66,9 +66,9 @@ fun RoomAliasResolverView( ResolvedRoomAliasView( resolvedRoomAlias = state.resolveState, onSuccess = onSuccess, - onRetry = { state.eventSink(RoomAliasResolverEvents.Retry) }, + onRetry = { state.eventSink(RoomAliasResolverEvent.Retry) }, onDismissError = { - state.eventSink(RoomAliasResolverEvents.DismissError) + state.eventSink(RoomAliasResolverEvent.DismissError) onBackClick() } ) diff --git a/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasHelperPresenterTest.kt b/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasHelperPresenterTest.kt index a50049446b..d480ffce11 100644 --- a/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasHelperPresenterTest.kt +++ b/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasHelperPresenterTest.kt @@ -91,7 +91,7 @@ class RoomAliasHelperPresenterTest { assertThat(awaitItem().resolveState.isLoading()).isTrue() val resultState = awaitItem() assertThat(resultState.resolveState.errorOrNull()).isEqualTo(AN_EXCEPTION) - resultState.eventSink(RoomAliasResolverEvents.Retry) + resultState.eventSink(RoomAliasResolverEvent.Retry) val retryLoadingState = awaitItem() assertThat(retryLoadingState.resolveState.isLoading()).isTrue() val retryState = awaitItem() diff --git a/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasHelperViewTest.kt b/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasHelperViewTest.kt index ae33517732..18a97ebb69 100644 --- a/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasHelperViewTest.kt +++ b/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasHelperViewTest.kt @@ -30,7 +30,7 @@ import org.junit.Test class RoomAliasHelperViewTest : RobolectricTest() { @Test fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setRoomAliasResolverView( aRoomAliasResolverState( @@ -44,7 +44,7 @@ class RoomAliasHelperViewTest : RobolectricTest() { @Test fun `clicking on Retry emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setRoomAliasResolverView( aRoomAliasResolverState( resolveState = AsyncData.Failure(Exception("Error")), @@ -52,13 +52,13 @@ class RoomAliasHelperViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_retry) - eventsRecorder.assertSingle(RoomAliasResolverEvents.Retry) + eventsRecorder.assertSingle(RoomAliasResolverEvent.Retry) } @Test fun `success state invokes the expected Callback`() = runAndroidComposeUiTest { val result = aResolvedRoomAlias() - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithParam(result) { setRoomAliasResolverView( aRoomAliasResolverState( From 4e9ec1b631609a2cc885bd052fc1ef72464f880e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:17:21 +0200 Subject: [PATCH 049/112] `RoomDirectoryEvents` -> `RoomDirectoryEvent` --- .../{RoomDirectoryEvents.kt => RoomDirectoryEvent.kt} | 6 +++--- .../roomdirectory/impl/root/RoomDirectoryPresenter.kt | 6 +++--- .../roomdirectory/impl/root/RoomDirectoryState.kt | 2 +- .../impl/root/RoomDirectoryStatePreviewParam.kt | 2 +- .../roomdirectory/impl/root/RoomDirectoryView.kt | 4 ++-- .../impl/root/RoomDirectoryPresenterTest.kt | 4 ++-- .../roomdirectory/impl/root/RoomDirectoryViewTest.kt | 10 +++++----- 7 files changed, 17 insertions(+), 17 deletions(-) rename features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/{RoomDirectoryEvents.kt => RoomDirectoryEvent.kt} (67%) diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryEvents.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryEvent.kt similarity index 67% rename from features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryEvents.kt rename to features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryEvent.kt index 20e866f2bf..8eb82d8ea9 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryEvents.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryEvent.kt @@ -8,7 +8,7 @@ package io.element.android.features.roomdirectory.impl.root -sealed interface RoomDirectoryEvents { - data class Search(val query: String) : RoomDirectoryEvents - data object LoadMore : RoomDirectoryEvents +sealed interface RoomDirectoryEvent { + data class Search(val query: String) : RoomDirectoryEvent + data object LoadMore : RoomDirectoryEvent } diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt index 98338440a1..b99a31dced 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt @@ -63,12 +63,12 @@ class RoomDirectoryPresenter( loadingMore = false } } - fun handleEvent(event: RoomDirectoryEvents) { + fun handleEvent(event: RoomDirectoryEvent) { when (event) { - RoomDirectoryEvents.LoadMore -> { + RoomDirectoryEvent.LoadMore -> { loadingMore = true } - is RoomDirectoryEvents.Search -> { + is RoomDirectoryEvent.Search -> { searchQuery = event.query } } diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryState.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryState.kt index a3647381dc..4066ff785a 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryState.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryState.kt @@ -15,7 +15,7 @@ data class RoomDirectoryState( val query: String, val roomDescriptions: ImmutableList, val displayLoadMoreIndicator: Boolean, - val eventSink: (RoomDirectoryEvents) -> Unit + val eventSink: (RoomDirectoryEvent) -> Unit ) { val displayEmptyState = roomDescriptions.isEmpty() && !displayLoadMoreIndicator } diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStatePreviewParam.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStatePreviewParam.kt index d7869f3d06..89dde9605b 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStatePreviewParam.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStatePreviewParam.kt @@ -35,7 +35,7 @@ fun aRoomDirectoryState( query: String = "", displayLoadMoreIndicator: Boolean = false, roomDescriptions: ImmutableList = persistentListOf(), - eventSink: (RoomDirectoryEvents) -> Unit = {}, + eventSink: (RoomDirectoryEvent) -> Unit = {}, ) = RoomDirectoryState( query = query, roomDescriptions = roomDescriptions, diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt index b880a4d28e..3e9c6e05c4 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt @@ -107,7 +107,7 @@ private fun RoomDirectoryContent( Column(modifier = modifier) { SearchTextField( query = state.query, - onQueryChange = { state.eventSink(RoomDirectoryEvents.Search(it)) }, + onQueryChange = { state.eventSink(RoomDirectoryEvent.Search(it)) }, placeholder = stringResource(id = CommonStrings.action_search), modifier = Modifier.fillMaxWidth(), ) @@ -116,7 +116,7 @@ private fun RoomDirectoryContent( displayLoadMoreIndicator = state.displayLoadMoreIndicator, displayEmptyState = state.displayEmptyState, onResultClick = onResultClick, - onReachedLoadMore = { state.eventSink(RoomDirectoryEvents.LoadMore) }, + onReachedLoadMore = { state.eventSink(RoomDirectoryEvent.LoadMore) }, ) } } diff --git a/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenterTest.kt b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenterTest.kt index 6295e4beb6..7d75703587 100644 --- a/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenterTest.kt +++ b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenterTest.kt @@ -91,7 +91,7 @@ class RoomDirectoryPresenterTest { val presenter = createRoomDirectoryPresenter(roomDirectoryService = roomDirectoryService) presenter.test { awaitItem().also { state -> - state.eventSink(RoomDirectoryEvents.Search("test")) + state.eventSink(RoomDirectoryEvent.Search("test")) } awaitItem().also { state -> assertThat(state.query).isEqualTo("test") @@ -112,7 +112,7 @@ class RoomDirectoryPresenterTest { val presenter = createRoomDirectoryPresenter(roomDirectoryService = roomDirectoryService) presenter.test { awaitItem().also { state -> - state.eventSink(RoomDirectoryEvents.LoadMore) + state.eventSink(RoomDirectoryEvent.LoadMore) } advanceUntilIdle() cancelAndIgnoreRemainingEvents() diff --git a/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryViewTest.kt b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryViewTest.kt index b3b935596f..1c26290f10 100644 --- a/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryViewTest.kt +++ b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryViewTest.kt @@ -30,7 +30,7 @@ import org.junit.Test class RoomDirectoryViewTest : RobolectricTest() { @Test fun `typing text in search field emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setRoomDirectoryView( state = aRoomDirectoryState( eventSink = eventsRecorder, @@ -39,12 +39,12 @@ class RoomDirectoryViewTest : RobolectricTest() { onNodeWithTag(TestTags.searchTextField.value).performTextInput( text = "Test" ) - eventsRecorder.assertSingle(RoomDirectoryEvents.Search("Test")) + eventsRecorder.assertSingle(RoomDirectoryEvent.Search("Test")) } @Test fun `clicking on room item then onResultClick lambda is called once`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val state = aRoomDirectoryState( roomDescriptions = aRoomDescriptionList(), eventSink = eventsRecorder, @@ -61,13 +61,13 @@ class RoomDirectoryViewTest : RobolectricTest() { @Test fun `composing load more indicator emits expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() val state = aRoomDirectoryState( displayLoadMoreIndicator = true, eventSink = eventsRecorder, ) setRoomDirectoryView(state = state) - eventsRecorder.assertSingle(RoomDirectoryEvents.LoadMore) + eventsRecorder.assertSingle(RoomDirectoryEvent.LoadMore) } } From 17213ac93e4782118bc372bd7d660c50b20800ec Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:18:21 +0200 Subject: [PATCH 050/112] `RoomMemberModerationEvents` -> `RoomMemberModerationEvent` --- .../features/messages/impl/MessagesNode.kt | 4 +-- .../messages/impl/MessagesPresenter.kt | 4 +-- .../impl/MessagesStatePreviewParam.kt | 4 +-- .../impl/threads/ThreadedMessagesNode.kt | 4 +-- .../impl/members/RoomMemberListNode.kt | 4 +-- .../impl/members/RoomMemberListPresenter.kt | 2 +- .../RoomMemberListStatePreviewParam.kt | 4 +-- ...Events.kt => RoomMemberModerationEvent.kt} | 6 ++-- .../api/RoomMemberModerationState.kt | 2 +- .../InternalRoomMemberModerationEvents.kt | 4 +-- .../impl/InternalRoomMemberModerationState.kt | 4 +-- ...alRoomMemberModerationStatePreviewParam.kt | 4 +-- .../impl/RoomMemberModerationPresenter.kt | 8 +++--- .../impl/RoomMemberModerationPresenterTest.kt | 28 +++++++++---------- .../impl/RoomMemberModerationViewTest.kt | 24 ++++++++-------- 15 files changed, 53 insertions(+), 53 deletions(-) rename features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/{RoomMemberModerationEvents.kt => RoomMemberModerationEvent.kt} (84%) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index d4fce982e8..97d3465ce7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -43,7 +43,7 @@ import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPr import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.roommembermoderation.api.ModerationAction -import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents +import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvent import io.element.android.features.roommembermoderation.api.RoomMemberModerationRenderer import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.androidutils.system.openUrlInExternalApp @@ -348,7 +348,7 @@ class MessagesNode( onSelectAction = { action, target -> when (action) { is ModerationAction.DisplayProfile -> callback.navigateToRoomMemberDetails(target.userId) - else -> state.roomMemberModerationState.eventSink(RoomMemberModerationEvents.ProcessAction(action, target)) + else -> state.roomMemberModerationState.eventSink(RoomMemberModerationEvent.ProcessAction(action, target)) } }, onAvatarClick = { user -> 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 4af0c05955..3ba8099ecb 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 @@ -54,7 +54,7 @@ import io.element.android.features.messages.impl.timeline.model.event.captionOrN import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState import io.element.android.features.messages.impl.voicemessages.composer.DefaultVoiceMessageComposerPresenter import io.element.android.features.roomcall.api.RoomCallState -import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents +import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvent import io.element.android.features.roommembermoderation.api.RoomMemberModerationState import io.element.android.libraries.androidutils.clipboard.ClipboardHelper import io.element.android.libraries.architecture.AsyncData @@ -263,7 +263,7 @@ class MessagesPresenter( } } is MessagesEvent.OnUserClicked -> { - roomMemberModerationState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(event.user)) + roomMemberModerationState.eventSink(RoomMemberModerationEvent.ShowActionsForUser(event.user)) } MessagesEvent.StopLiveLocationShare -> { localCoroutineScope.launch { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStatePreviewParam.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStatePreviewParam.kt index 8ab7e64121..d792ab5ed4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStatePreviewParam.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStatePreviewParam.kt @@ -38,7 +38,7 @@ import io.element.android.features.messages.impl.timeline.protection.TimelinePro import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState import io.element.android.features.roomcall.api.RoomCallState import io.element.android.features.roomcall.api.aStandByCallState -import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents +import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvent import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions import io.element.android.features.roommembermoderation.api.RoomMemberModerationState import io.element.android.libraries.architecture.AsyncData @@ -168,7 +168,7 @@ fun aRoomMemberModerationState( permissions: RoomMemberModerationPermissions = RoomMemberModerationPermissions.DEFAULT, ) = object : RoomMemberModerationState { override val permissions: RoomMemberModerationPermissions = permissions - override val eventSink: (RoomMemberModerationEvents) -> Unit = {} + override val eventSink: (RoomMemberModerationEvent) -> Unit = {} } fun aUserEventPermissions( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt index b7fd9f572f..cf3c701015 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt @@ -46,7 +46,7 @@ import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPr import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.roommembermoderation.api.ModerationAction -import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents +import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvent import io.element.android.features.roommembermoderation.api.RoomMemberModerationRenderer import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.androidutils.system.openUrlInExternalApp @@ -343,7 +343,7 @@ class ThreadedMessagesNode( onSelectAction = { action, target -> when (action) { is ModerationAction.DisplayProfile -> callback.navigateToRoomMemberDetails(target.userId) - else -> state.roomMemberModerationState.eventSink(RoomMemberModerationEvents.ProcessAction(action, target)) + else -> state.roomMemberModerationState.eventSink(RoomMemberModerationEvent.ProcessAction(action, target)) } }, onAvatarClick = { user -> diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListNode.kt index 37f107e784..402ff5ed02 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListNode.kt @@ -21,7 +21,7 @@ import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode import io.element.android.features.roommembermoderation.api.ModerationAction -import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents +import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvent import io.element.android.features.roommembermoderation.api.RoomMemberModerationRenderer import io.element.android.libraries.architecture.appyx.launchMolecule import io.element.android.libraries.architecture.callback @@ -81,7 +81,7 @@ class RoomMemberListNode( onSelectAction = { action, target -> when (action) { is ModerationAction.DisplayProfile -> openRoomMemberDetails(target.userId) - else -> state.moderationState.eventSink(RoomMemberModerationEvents.ProcessAction(action, target)) + else -> state.moderationState.eventSink(RoomMemberModerationEvent.ProcessAction(action, target)) } }, onAvatarClick = { user -> 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 674dd9aad9..a47f9af84f 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 @@ -19,7 +19,7 @@ import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import dev.zacsweers.metro.Inject -import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents.ShowActionsForUser +import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvent.ShowActionsForUser import io.element.android.features.roommembermoderation.api.RoomMemberModerationState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStatePreviewParam.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStatePreviewParam.kt index 59a26e6207..39e6ffdace 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStatePreviewParam.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStatePreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.roomdetails.impl.members import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents +import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvent import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions import io.element.android.features.roommembermoderation.api.RoomMemberModerationState import io.element.android.libraries.architecture.AsyncData @@ -115,7 +115,7 @@ fun aRoomMemberModerationState( canBan = canBan, canKick = canKick, ) - override val eventSink: (RoomMemberModerationEvents) -> Unit = {} + override val eventSink: (RoomMemberModerationEvent) -> Unit = {} } } diff --git a/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationEvents.kt b/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationEvent.kt similarity index 84% rename from features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationEvents.kt rename to features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationEvent.kt index d452ff4e66..403268a18d 100644 --- a/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationEvents.kt +++ b/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationEvent.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser /** * Events sent to the moderation presenter through [RoomMemberModerationState.eventSink]. */ -interface RoomMemberModerationEvents { - data class ShowActionsForUser(val user: MatrixUser) : RoomMemberModerationEvents - data class ProcessAction(val action: ModerationAction, val targetUser: MatrixUser) : RoomMemberModerationEvents +interface RoomMemberModerationEvent { + data class ShowActionsForUser(val user: MatrixUser) : RoomMemberModerationEvent + data class ProcessAction(val action: ModerationAction, val targetUser: MatrixUser) : RoomMemberModerationEvent } diff --git a/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationState.kt b/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationState.kt index 9227c77edf..8c73aaee0e 100644 --- a/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationState.kt +++ b/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationState.kt @@ -19,7 +19,7 @@ interface RoomMemberModerationState { val permissions: RoomMemberModerationPermissions /** Where the host screen sends its events to open the actions of a member or run one of them. */ - val eventSink: (RoomMemberModerationEvents) -> Unit + val eventSink: (RoomMemberModerationEvent) -> Unit } data class ModerationActionState( diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationEvents.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationEvents.kt index 2bc76db761..8cfdb30f88 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationEvents.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationEvents.kt @@ -8,9 +8,9 @@ package io.element.android.features.roommembermoderation.impl -import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents +import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvent -sealed interface InternalRoomMemberModerationEvents : RoomMemberModerationEvents { +sealed interface InternalRoomMemberModerationEvents : RoomMemberModerationEvent { data class DoKickUser(val reason: String) : InternalRoomMemberModerationEvents data class DoBanUser(val reason: String) : InternalRoomMemberModerationEvents data class DoUnbanUser(val reason: String) : InternalRoomMemberModerationEvents diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationState.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationState.kt index f45a277992..adb9e84e1d 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationState.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationState.kt @@ -9,7 +9,7 @@ package io.element.android.features.roommembermoderation.impl import io.element.android.features.roommembermoderation.api.ModerationActionState -import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents +import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvent import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions import io.element.android.features.roommembermoderation.api.RoomMemberModerationState import io.element.android.libraries.architecture.AsyncAction @@ -23,7 +23,7 @@ data class InternalRoomMemberModerationState( val kickUserAsyncAction: AsyncAction, val banUserAsyncAction: AsyncAction, val unbanUserAsyncAction: AsyncAction, - override val eventSink: (RoomMemberModerationEvents) -> Unit, + override val eventSink: (RoomMemberModerationEvent) -> Unit, ) : RoomMemberModerationState { val canDisplayActions = actions.isNotEmpty() } diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStatePreviewParam.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStatePreviewParam.kt index 78358da213..1da2392737 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStatePreviewParam.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStatePreviewParam.kt @@ -11,7 +11,7 @@ package io.element.android.features.roommembermoderation.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.roommembermoderation.api.ModerationAction import io.element.android.features.roommembermoderation.api.ModerationActionState -import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents +import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvent import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE @@ -93,7 +93,7 @@ fun aRoomMembersModerationState( kickUserAsyncAction: AsyncAction = AsyncAction.Uninitialized, banUserAsyncAction: AsyncAction = AsyncAction.Uninitialized, unbanUserAsyncAction: AsyncAction = AsyncAction.Uninitialized, - eventSink: (RoomMemberModerationEvents) -> Unit = {}, + eventSink: (RoomMemberModerationEvent) -> Unit = {}, ) = InternalRoomMemberModerationState( permissions = permissions, selectedUser = selectedUser, diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt index 2a876fc484..f4094dbc8e 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt @@ -20,7 +20,7 @@ import dev.zacsweers.metro.Inject import im.vector.app.features.analytics.plan.RoomModeration import io.element.android.features.roommembermoderation.api.ModerationAction import io.element.android.features.roommembermoderation.api.ModerationActionState -import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents +import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvent import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions import io.element.android.features.roommembermoderation.api.RoomMemberModerationState import io.element.android.features.roommembermoderation.api.roomMemberModerationPermissions @@ -77,9 +77,9 @@ class RoomMemberModerationPresenter( } val moderationActions = remember { mutableStateOf>(persistentListOf()) } - fun handleEvent(event: RoomMemberModerationEvents) { + fun handleEvent(event: RoomMemberModerationEvent) { when (event) { - is RoomMemberModerationEvents.ShowActionsForUser -> { + is RoomMemberModerationEvent.ShowActionsForUser -> { selectedUser = event.user val member = room.membersStateFlow.value.roomMembers()?.firstOrNull { it.userId == event.user.userId @@ -90,7 +90,7 @@ class RoomMemberModerationPresenter( currentUserPowerLevel = currentUserPowerLevel, ) } - is RoomMemberModerationEvents.ProcessAction -> { + is RoomMemberModerationEvent.ProcessAction -> { // First, hide any list of existing actions that could be displayed moderationActions.value = persistentListOf() diff --git a/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenterTest.kt b/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenterTest.kt index 3397f3a40a..56af977246 100644 --- a/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenterTest.kt +++ b/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenterTest.kt @@ -12,7 +12,7 @@ import app.cash.turbine.TurbineTestContext import com.google.common.truth.Truth.assertThat import io.element.android.features.roommembermoderation.api.ModerationAction import io.element.android.features.roommembermoderation.api.ModerationActionState -import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents +import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvent import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions import io.element.android.features.roommembermoderation.api.RoomMemberModerationState import io.element.android.libraries.architecture.AsyncAction @@ -73,7 +73,7 @@ class RoomMemberModerationPresenterTest { ) createRoomMemberModerationPresenter(room = room).test { val initialState = awaitState() - initialState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(targetUser)) + initialState.eventSink(RoomMemberModerationEvent.ShowActionsForUser(targetUser)) skipItems(1) val updatedState = awaitState() assertThat(updatedState.selectedUser).isEqualTo(targetUser) @@ -93,7 +93,7 @@ class RoomMemberModerationPresenterTest { ) createRoomMemberModerationPresenter(room = room).test { val initialState = awaitState() - initialState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(targetUser)) + initialState.eventSink(RoomMemberModerationEvent.ShowActionsForUser(targetUser)) skipItems(2) val updatedState = awaitState() assertThat(updatedState.selectedUser).isEqualTo(targetUser) @@ -115,7 +115,7 @@ class RoomMemberModerationPresenterTest { ) createRoomMemberModerationPresenter(room = room).test { val initialState = awaitState() - initialState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(targetUser)) + initialState.eventSink(RoomMemberModerationEvent.ShowActionsForUser(targetUser)) skipItems(2) val updatedState = awaitState() assertThat(updatedState.selectedUser).isEqualTo(targetUser) @@ -137,7 +137,7 @@ class RoomMemberModerationPresenterTest { ) createRoomMemberModerationPresenter(room = room).test { val initialState = awaitState() - initialState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(targetUser)) + initialState.eventSink(RoomMemberModerationEvent.ShowActionsForUser(targetUser)) skipItems(2) val updatedState = awaitState() assertThat(updatedState.selectedUser).isEqualTo(targetUser) @@ -159,7 +159,7 @@ class RoomMemberModerationPresenterTest { ) createRoomMemberModerationPresenter(room = room).test { val initialState = awaitState() - initialState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(targetUser)) + initialState.eventSink(RoomMemberModerationEvent.ShowActionsForUser(targetUser)) skipItems(2) val updatedState = awaitState() assertThat(updatedState.selectedUser).isEqualTo(targetUser) @@ -175,7 +175,7 @@ class RoomMemberModerationPresenterTest { createRoomMemberModerationPresenter(room = aJoinedRoom()).test { val initialState = awaitState() initialState.eventSink( - RoomMemberModerationEvents.ProcessAction( + RoomMemberModerationEvent.ProcessAction( targetUser = targetUser, action = ModerationAction.KickUser ) @@ -192,7 +192,7 @@ class RoomMemberModerationPresenterTest { createRoomMemberModerationPresenter(room = aJoinedRoom()).test { val initialState = awaitState() initialState.eventSink( - RoomMemberModerationEvents.ProcessAction( + RoomMemberModerationEvent.ProcessAction( targetUser = targetUser, action = ModerationAction.BanUser ) @@ -209,7 +209,7 @@ class RoomMemberModerationPresenterTest { createRoomMemberModerationPresenter(room = aJoinedRoom()).test { val initialState = awaitState() initialState.eventSink( - RoomMemberModerationEvents.ProcessAction( + RoomMemberModerationEvent.ProcessAction( targetUser = targetUser, action = ModerationAction.UnbanUser ) @@ -235,7 +235,7 @@ class RoomMemberModerationPresenterTest { createRoomMemberModerationPresenter(room = room).test { val initialState = awaitState() initialState.eventSink( - RoomMemberModerationEvents.ProcessAction( + RoomMemberModerationEvent.ProcessAction( targetUser = targetUser, action = ModerationAction.KickUser ) @@ -265,7 +265,7 @@ class RoomMemberModerationPresenterTest { createRoomMemberModerationPresenter(room = room).test { val initialState = awaitState() initialState.eventSink( - RoomMemberModerationEvents.ProcessAction( + RoomMemberModerationEvent.ProcessAction( targetUser = targetUser, action = ModerationAction.BanUser ) @@ -295,7 +295,7 @@ class RoomMemberModerationPresenterTest { createRoomMemberModerationPresenter(room = room).test { val initialState = awaitState() initialState.eventSink( - RoomMemberModerationEvents.ProcessAction( + RoomMemberModerationEvent.ProcessAction( targetUser = targetUser, action = ModerationAction.UnbanUser ) @@ -320,7 +320,7 @@ class RoomMemberModerationPresenterTest { createRoomMemberModerationPresenter(room = room).test { val initialState = awaitState() initialState.eventSink( - RoomMemberModerationEvents.ProcessAction( + RoomMemberModerationEvent.ProcessAction( targetUser = targetUser, action = ModerationAction.KickUser ) @@ -340,7 +340,7 @@ class RoomMemberModerationPresenterTest { createRoomMemberModerationPresenter(room = aJoinedRoom()).test { val initialState = awaitState() initialState.eventSink( - RoomMemberModerationEvents.ProcessAction(targetUser = targetUser, action = ModerationAction.BanUser) + RoomMemberModerationEvent.ProcessAction(targetUser = targetUser, action = ModerationAction.BanUser) ) skipItems(2) initialState.eventSink(InternalRoomMemberModerationEvents.Reset) diff --git a/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationViewTest.kt b/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationViewTest.kt index f99e517767..96095e3e77 100644 --- a/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationViewTest.kt +++ b/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationViewTest.kt @@ -16,7 +16,7 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.v2.runAndroidComposeUiTest import io.element.android.features.roommembermoderation.api.ModerationAction import io.element.android.features.roommembermoderation.api.ModerationActionState -import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents +import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvent import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.testtags.TestTags @@ -34,7 +34,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { @Test fun `clicking on display profile action calls onSelectAction`() = runAndroidComposeUiTest { val user = anAlice() - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithTwoParams(ModerationAction.DisplayProfile, user) { callback -> setRoomMemberModerationView( aRoomMembersModerationState( @@ -55,7 +55,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { @Test fun `clicking on kick user action calls onSelectAction`() = runAndroidComposeUiTest { val user = anAlice() - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithTwoParams(ModerationAction.KickUser, user) { callback -> setRoomMemberModerationView( aRoomMembersModerationState( @@ -76,7 +76,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { @Test fun `clicking on ban user action calls onSelectAction`() = runAndroidComposeUiTest { val user = anAlice() - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithTwoParams(ModerationAction.BanUser, user) { callback -> setRoomMemberModerationView( aRoomMembersModerationState( @@ -97,7 +97,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { @Test fun `clicking on unban user action calls onSelectAction`() = runAndroidComposeUiTest { val user = anAlice() - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithTwoParams(ModerationAction.UnbanUser, user) { callback -> setRoomMemberModerationView( aRoomMembersModerationState( @@ -117,7 +117,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { @Test fun `clicking submit on kick confirmation dialog sends DoKickUser event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = anAlice(), @@ -131,7 +131,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { @Test fun `clicking dismiss on kick confirmation dialog sends Reset event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = anAlice(), @@ -145,7 +145,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { @Test fun `clicking submit on ban confirmation dialog sends DoBanUser event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = anAlice(), @@ -159,7 +159,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { @Test fun `clicking dismiss on ban confirmation dialog sends Reset event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = anAlice(), @@ -173,7 +173,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { @Test fun `clicking confirm on unban confirmation dialog sends DoUnbanUser event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = anAlice(), @@ -187,7 +187,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { @Test fun `clicking dismiss on unban confirmation dialog sends Reset event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = anAlice(), @@ -201,7 +201,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { @Test fun `disabled actions are not clickable`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = anAlice(), From 4981a0451922d0e6920b4e2d66b2625c87a5a771 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:19:06 +0200 Subject: [PATCH 051/112] `InternalRoomMemberModerationEvents` -> `InternalRoomMemberModerationEvent` --- ...nts.kt => InternalRoomMemberModerationEvent.kt} | 10 +++++----- .../impl/RoomMemberModerationPresenter.kt | 8 ++++---- .../impl/RoomMemberModerationView.kt | 14 +++++++------- .../impl/RoomMemberModerationPresenterTest.kt | 10 +++++----- .../impl/RoomMemberModerationViewTest.kt | 12 ++++++------ 5 files changed, 27 insertions(+), 27 deletions(-) rename features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/{InternalRoomMemberModerationEvents.kt => InternalRoomMemberModerationEvent.kt} (74%) diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationEvents.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationEvent.kt similarity index 74% rename from features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationEvents.kt rename to features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationEvent.kt index 8cfdb30f88..741cb42961 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationEvents.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationEvent.kt @@ -10,9 +10,9 @@ package io.element.android.features.roommembermoderation.impl import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvent -sealed interface InternalRoomMemberModerationEvents : RoomMemberModerationEvent { - data class DoKickUser(val reason: String) : InternalRoomMemberModerationEvents - data class DoBanUser(val reason: String) : InternalRoomMemberModerationEvents - data class DoUnbanUser(val reason: String) : InternalRoomMemberModerationEvents - data object Reset : InternalRoomMemberModerationEvents +sealed interface InternalRoomMemberModerationEvent : RoomMemberModerationEvent { + data class DoKickUser(val reason: String) : InternalRoomMemberModerationEvent + data class DoBanUser(val reason: String) : InternalRoomMemberModerationEvent + data class DoUnbanUser(val reason: String) : InternalRoomMemberModerationEvent + data object Reset : InternalRoomMemberModerationEvent } diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt index f4094dbc8e..49d7ced4d7 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt @@ -110,25 +110,25 @@ class RoomMemberModerationPresenter( } } } - is InternalRoomMemberModerationEvents.DoKickUser -> { + is InternalRoomMemberModerationEvent.DoKickUser -> { selectedUser?.let { coroutineScope.kickUser(it.userId, event.reason, kickUserAsyncAction) } selectedUser = null } - is InternalRoomMemberModerationEvents.DoBanUser -> { + is InternalRoomMemberModerationEvent.DoBanUser -> { selectedUser?.let { coroutineScope.banUser(it.userId, event.reason, banUserAsyncAction) } selectedUser = null } - is InternalRoomMemberModerationEvents.DoUnbanUser -> { + is InternalRoomMemberModerationEvent.DoUnbanUser -> { selectedUser?.let { coroutineScope.unbanUser(it.userId, event.reason, unbanUserAsyncAction) } selectedUser = null } - is InternalRoomMemberModerationEvents.Reset -> { + is InternalRoomMemberModerationEvent.Reset -> { selectedUser = null moderationActions.value = persistentListOf() kickUserAsyncAction.value = AsyncAction.Uninitialized diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt index f1c6cc8ed3..3ec00fd232 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt @@ -77,7 +77,7 @@ fun RoomMemberModerationView( actions = state.actions, onSelectAction = onSelectAction, onAvatarClick = onAvatarClick, - onDismiss = { state.eventSink(InternalRoomMemberModerationEvents.Reset) }, + onDismiss = { state.eventSink(InternalRoomMemberModerationEvent.Reset) }, ) } RoomMemberAsyncActions(state = state) @@ -102,9 +102,9 @@ private fun RoomMemberAsyncActions( destructiveSubmit = true, minLines = 2, onSubmit = { reason -> - state.eventSink(InternalRoomMemberModerationEvents.DoKickUser(reason = reason)) + state.eventSink(InternalRoomMemberModerationEvent.DoKickUser(reason = reason)) }, - onDismissRequest = { state.eventSink(InternalRoomMemberModerationEvents.Reset) }, + onDismissRequest = { state.eventSink(InternalRoomMemberModerationEvent.Reset) }, placeholder = stringResource(id = CommonStrings.common_reason), content = stringResource(R.string.screen_bottom_sheet_manage_room_member_kick_member_confirmation_description), value = "", @@ -142,9 +142,9 @@ private fun RoomMemberAsyncActions( destructiveSubmit = true, minLines = 2, onSubmit = { reason -> - state.eventSink(InternalRoomMemberModerationEvents.DoBanUser(reason = reason)) + state.eventSink(InternalRoomMemberModerationEvent.DoBanUser(reason = reason)) }, - onDismissRequest = { state.eventSink(InternalRoomMemberModerationEvents.Reset) }, + onDismissRequest = { state.eventSink(InternalRoomMemberModerationEvent.Reset) }, placeholder = stringResource(id = CommonStrings.common_reason), content = stringResource(R.string.screen_bottom_sheet_manage_room_member_ban_member_confirmation_description), value = "", @@ -185,9 +185,9 @@ private fun RoomMemberAsyncActions( asyncIndicatorState.enqueue { AsyncIndicator.Loading(text = stringResource(R.string.screen_bottom_sheet_manage_room_member_unbanning_user, userDisplayName)) } - state.eventSink(InternalRoomMemberModerationEvents.DoUnbanUser(reason = reason)) + state.eventSink(InternalRoomMemberModerationEvent.DoUnbanUser(reason = reason)) }, - onDismissRequest = { state.eventSink(InternalRoomMemberModerationEvents.Reset) }, + onDismissRequest = { state.eventSink(InternalRoomMemberModerationEvent.Reset) }, placeholder = stringResource(id = CommonStrings.common_reason), content = stringResource(R.string.screen_bottom_sheet_manage_room_member_unban_member_confirmation_description), value = "", diff --git a/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenterTest.kt b/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenterTest.kt index 56af977246..1f13b7e68f 100644 --- a/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenterTest.kt +++ b/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenterTest.kt @@ -241,7 +241,7 @@ class RoomMemberModerationPresenterTest { ) ) skipItems(2) - initialState.eventSink(InternalRoomMemberModerationEvents.DoKickUser("Reason")) + initialState.eventSink(InternalRoomMemberModerationEvent.DoKickUser("Reason")) skipItems(1) val loadingState = awaitState() assertThat(loadingState.kickUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java) @@ -271,7 +271,7 @@ class RoomMemberModerationPresenterTest { ) ) skipItems(2) - initialState.eventSink(InternalRoomMemberModerationEvents.DoBanUser("Reason")) + initialState.eventSink(InternalRoomMemberModerationEvent.DoBanUser("Reason")) skipItems(1) val loadingState = awaitState() assertThat(loadingState.banUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java) @@ -301,7 +301,7 @@ class RoomMemberModerationPresenterTest { ) ) skipItems(2) - initialState.eventSink(InternalRoomMemberModerationEvents.DoUnbanUser("Reason")) + initialState.eventSink(InternalRoomMemberModerationEvent.DoUnbanUser("Reason")) skipItems(1) val loadingState = awaitState() assertThat(loadingState.unbanUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java) @@ -326,7 +326,7 @@ class RoomMemberModerationPresenterTest { ) ) skipItems(2) - initialState.eventSink(InternalRoomMemberModerationEvents.DoKickUser("Reason")) + initialState.eventSink(InternalRoomMemberModerationEvent.DoKickUser("Reason")) skipItems(1) val loadingState = awaitState() assertThat(loadingState.kickUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java) @@ -343,7 +343,7 @@ class RoomMemberModerationPresenterTest { RoomMemberModerationEvent.ProcessAction(targetUser = targetUser, action = ModerationAction.BanUser) ) skipItems(2) - initialState.eventSink(InternalRoomMemberModerationEvents.Reset) + initialState.eventSink(InternalRoomMemberModerationEvent.Reset) skipItems(1) val resetState = awaitState() assertThat(resetState.selectedUser).isNull() diff --git a/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationViewTest.kt b/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationViewTest.kt index 96095e3e77..ff7183ed5f 100644 --- a/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationViewTest.kt +++ b/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationViewTest.kt @@ -126,7 +126,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { ), ) pressTag(TestTags.dialogPositive.value) - eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.DoKickUser(reason = "")) + eventsRecorder.assertSingle(InternalRoomMemberModerationEvent.DoKickUser(reason = "")) } @Test @@ -140,7 +140,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { ), ) pressTag(TestTags.dialogNegative.value) - eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.Reset) + eventsRecorder.assertSingle(InternalRoomMemberModerationEvent.Reset) } @Test @@ -154,7 +154,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { ), ) pressTag(TestTags.dialogPositive.value) - eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.DoBanUser(reason = "")) + eventsRecorder.assertSingle(InternalRoomMemberModerationEvent.DoBanUser(reason = "")) } @Test @@ -168,7 +168,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { ), ) pressTag(TestTags.dialogNegative.value) - eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.Reset) + eventsRecorder.assertSingle(InternalRoomMemberModerationEvent.Reset) } @Test @@ -182,7 +182,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { ), ) pressTag(TestTags.dialogPositive.value) - eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.DoUnbanUser("")) + eventsRecorder.assertSingle(InternalRoomMemberModerationEvent.DoUnbanUser("")) } @Test @@ -196,7 +196,7 @@ class RoomMemberModerationViewTest : RobolectricTest() { ), ) pressTag(TestTags.dialogNegative.value) - eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.Reset) + eventsRecorder.assertSingle(InternalRoomMemberModerationEvent.Reset) } @Test From c8c579ed7e68203257ac01d50751dddc13f3f574 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:19:58 +0200 Subject: [PATCH 052/112] `SecureBackupDisableEvents` -> `SecureBackupDisableEvent` --- ...reBackupDisableEvents.kt => SecureBackupDisableEvent.kt} | 6 +++--- .../impl/disable/SecureBackupDisablePresenter.kt | 6 +++--- .../securebackup/impl/disable/SecureBackupDisableState.kt | 2 +- .../securebackup/impl/disable/SecureBackupDisableView.kt | 4 ++-- .../impl/disable/SecureBackupDisablePresenterTest.kt | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) rename features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/{SecureBackupDisableEvents.kt => SecureBackupDisableEvent.kt} (65%) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableEvents.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableEvent.kt similarity index 65% rename from features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableEvents.kt rename to features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableEvent.kt index 05d05a4840..330faa47f5 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableEvents.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableEvent.kt @@ -8,7 +8,7 @@ package io.element.android.features.securebackup.impl.disable -sealed interface SecureBackupDisableEvents { - data object DisableBackup : SecureBackupDisableEvents - data object DismissDialogs : SecureBackupDisableEvents +sealed interface SecureBackupDisableEvent { + data object DisableBackup : SecureBackupDisableEvent + data object DismissDialogs : SecureBackupDisableEvent } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenter.kt index 67b6816097..13a2882fa8 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenter.kt @@ -37,10 +37,10 @@ class SecureBackupDisablePresenter( Timber.tag(loggerTagDisable.value).d("backupState: $backupState") val disableAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val coroutineScope = rememberCoroutineScope() - fun handleEvent(event: SecureBackupDisableEvents) { + fun handleEvent(event: SecureBackupDisableEvent) { when (event) { - is SecureBackupDisableEvents.DisableBackup -> coroutineScope.disableBackup(disableAction) - SecureBackupDisableEvents.DismissDialogs -> { + is SecureBackupDisableEvent.DisableBackup -> coroutineScope.disableBackup(disableAction) + SecureBackupDisableEvent.DismissDialogs -> { disableAction.value = AsyncAction.Uninitialized } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableState.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableState.kt index a4f6b210ab..1e1b1ec35e 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableState.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableState.kt @@ -15,5 +15,5 @@ data class SecureBackupDisableState( val backupState: BackupState, val disableAction: AsyncAction, val appName: String, - val eventSink: (SecureBackupDisableEvents) -> Unit + val eventSink: (SecureBackupDisableEvent) -> Unit ) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt index 423e2ed5da..10019caae6 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt @@ -55,7 +55,7 @@ fun SecureBackupDisableView( async = state.disableAction, progressDialog = {}, errorMessage = { it.message ?: it.toString() }, - onErrorDismiss = { state.eventSink.invoke(SecureBackupDisableEvents.DismissDialogs) }, + onErrorDismiss = { state.eventSink.invoke(SecureBackupDisableEvent.DismissDialogs) }, onSuccess = { onSuccess() }, ) } @@ -69,7 +69,7 @@ private fun ColumnScope.Buttons( showProgress = state.disableAction.isLoading(), destructive = true, modifier = Modifier.fillMaxWidth(), - onClick = { state.eventSink.invoke(SecureBackupDisableEvents.DisableBackup) } + onClick = { state.eventSink.invoke(SecureBackupDisableEvent.DisableBackup) } ) } diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenterTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenterTest.kt index 899726d806..69f54fc253 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenterTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenterTest.kt @@ -47,7 +47,7 @@ class SecureBackupDisablePresenterTest { }.test { val initialState = awaitItem() assertThat(initialState.disableAction).isEqualTo(AsyncAction.Uninitialized) - initialState.eventSink(SecureBackupDisableEvents.DisableBackup) + initialState.eventSink(SecureBackupDisableEvent.DisableBackup) val loadingState = awaitItem() assertThat(loadingState.disableAction).isInstanceOf(AsyncAction.Loading::class.java) val finalState = awaitItem() @@ -68,12 +68,12 @@ class SecureBackupDisablePresenterTest { }.test { val initialState = awaitItem() assertThat(initialState.disableAction).isEqualTo(AsyncAction.Uninitialized) - initialState.eventSink(SecureBackupDisableEvents.DisableBackup) + initialState.eventSink(SecureBackupDisableEvent.DisableBackup) val loadingState = awaitItem() assertThat(loadingState.disableAction).isInstanceOf(AsyncAction.Loading::class.java) val errorState = awaitItem() assertThat(errorState.disableAction).isInstanceOf(AsyncAction.Failure::class.java) - errorState.eventSink(SecureBackupDisableEvents.DismissDialogs) + errorState.eventSink(SecureBackupDisableEvent.DismissDialogs) val finalState = awaitItem() assertThat(finalState.disableAction).isEqualTo(AsyncAction.Uninitialized) } From 2d611475da11071f7b2399b224bde9f7c2d3945c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:20:48 +0200 Subject: [PATCH 053/112] `SecureBackupEnterRecoveryKeyEvents` -> `SecureBackupEnterRecoveryKeyEvent` --- ...s.kt => SecureBackupEnterRecoveryKeyEvent.kt} | 10 +++++----- .../SecureBackupEnterRecoveryKeyPresenter.kt | 10 +++++----- .../enter/SecureBackupEnterRecoveryKeyState.kt | 2 +- ...ureBackupEnterRecoveryKeyStatePreviewParam.kt | 2 +- .../enter/SecureBackupEnterRecoveryKeyView.kt | 10 +++++----- .../SecureBackupEnterRecoveryKeyPresenterTest.kt | 8 ++++---- .../SecureBackupEnterRecoveryKeyViewTest.kt | 16 ++++++++-------- 7 files changed, 29 insertions(+), 29 deletions(-) rename features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/{SecureBackupEnterRecoveryKeyEvents.kt => SecureBackupEnterRecoveryKeyEvent.kt} (62%) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyEvents.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyEvent.kt similarity index 62% rename from features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyEvents.kt rename to features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyEvent.kt index 644756b957..ac5aa751b6 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyEvents.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyEvent.kt @@ -8,9 +8,9 @@ package io.element.android.features.securebackup.impl.enter -sealed interface SecureBackupEnterRecoveryKeyEvents { - data class OnRecoveryKeyChange(val recoveryKey: String) : SecureBackupEnterRecoveryKeyEvents - data class ChangeRecoveryKeyFieldContentsVisibility(val visible: Boolean) : SecureBackupEnterRecoveryKeyEvents - data object Submit : SecureBackupEnterRecoveryKeyEvents - data object ClearDialog : SecureBackupEnterRecoveryKeyEvents +sealed interface SecureBackupEnterRecoveryKeyEvent { + data class OnRecoveryKeyChange(val recoveryKey: String) : SecureBackupEnterRecoveryKeyEvent + data class ChangeRecoveryKeyFieldContentsVisibility(val visible: Boolean) : SecureBackupEnterRecoveryKeyEvent + data object Submit : SecureBackupEnterRecoveryKeyEvent + data object ClearDialog : SecureBackupEnterRecoveryKeyEvent } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenter.kt index 9a71cf1727..bbc61cc283 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenter.kt @@ -45,12 +45,12 @@ class SecureBackupEnterRecoveryKeyPresenter( mutableStateOf(AsyncAction.Uninitialized) } - fun handleEvent(event: SecureBackupEnterRecoveryKeyEvents) { + fun handleEvent(event: SecureBackupEnterRecoveryKeyEvent) { when (event) { - SecureBackupEnterRecoveryKeyEvents.ClearDialog -> { + SecureBackupEnterRecoveryKeyEvent.ClearDialog -> { submitAction.value = AsyncAction.Uninitialized } - is SecureBackupEnterRecoveryKeyEvents.OnRecoveryKeyChange -> { + is SecureBackupEnterRecoveryKeyEvent.OnRecoveryKeyChange -> { val previousRecoveryKey = recoveryKey recoveryKey = if (previousRecoveryKey.isEmpty() && recoveryKeyTools.isRecoveryKeyFormatValid(event.recoveryKey)) { // A Recovery key has been entered, remove the spaces for a better rendering @@ -60,11 +60,11 @@ class SecureBackupEnterRecoveryKeyPresenter( event.recoveryKey } } - SecureBackupEnterRecoveryKeyEvents.Submit -> { + SecureBackupEnterRecoveryKeyEvent.Submit -> { // No need to remove the spaces, the SDK will do it. coroutineScope.submitRecoveryKey(recoveryKey, submitAction) } - is SecureBackupEnterRecoveryKeyEvents.ChangeRecoveryKeyFieldContentsVisibility -> { + is SecureBackupEnterRecoveryKeyEvent.ChangeRecoveryKeyFieldContentsVisibility -> { displayRecoveryKeyFieldContents = event.visible } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyState.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyState.kt index 45a6989c15..188419553a 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyState.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyState.kt @@ -15,5 +15,5 @@ data class SecureBackupEnterRecoveryKeyState( val recoveryKeyViewState: RecoveryKeyViewState, val isSubmitEnabled: Boolean, val submitAction: AsyncAction, - val eventSink: (SecureBackupEnterRecoveryKeyEvents) -> Unit + val eventSink: (SecureBackupEnterRecoveryKeyEvent) -> Unit ) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyStatePreviewParam.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyStatePreviewParam.kt index 5041f4ab92..90a040106c 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyStatePreviewParam.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyStatePreviewParam.kt @@ -30,7 +30,7 @@ fun aSecureBackupEnterRecoveryKeyState( isSubmitEnabled: Boolean = recoveryKey.isNotEmpty(), displayTextFieldContents: Boolean = true, submitAction: AsyncAction = AsyncAction.Uninitialized, - eventSink: (SecureBackupEnterRecoveryKeyEvents) -> Unit = {}, + eventSink: (SecureBackupEnterRecoveryKeyEvent) -> Unit = {}, ) = SecureBackupEnterRecoveryKeyState( recoveryKeyViewState = RecoveryKeyViewState( recoveryKeyUserStory = RecoveryKeyUserStory.Enter, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt index 823bbf5c80..db3f228f8b 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt @@ -41,7 +41,7 @@ fun SecureBackupEnterRecoveryKeyView( progressDialog = { }, errorTitle = { stringResource(id = R.string.screen_recovery_key_confirm_error_title) }, errorMessage = { stringResource(id = R.string.screen_recovery_key_confirm_error_content) }, - onErrorDismiss = { state.eventSink(SecureBackupEnterRecoveryKeyEvents.ClearDialog) }, + onErrorDismiss = { state.eventSink(SecureBackupEnterRecoveryKeyEvent.ClearDialog) }, ) FlowStepPage( @@ -68,13 +68,13 @@ private fun Content( state = state.recoveryKeyViewState, onClick = null, onChange = { - state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvents.OnRecoveryKeyChange(it)) + state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvent.OnRecoveryKeyChange(it)) }, onSubmit = { - state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvents.Submit) + state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvent.Submit) }, toggleRecoveryKeyVisibility = { - state.eventSink(SecureBackupEnterRecoveryKeyEvents.ChangeRecoveryKeyFieldContentsVisibility(it)) + state.eventSink(SecureBackupEnterRecoveryKeyEvent.ChangeRecoveryKeyFieldContentsVisibility(it)) } ) } @@ -89,7 +89,7 @@ private fun ColumnScope.Buttons( showProgress = state.submitAction.isLoading(), modifier = Modifier.fillMaxWidth(), onClick = { - state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvents.Submit) + state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvent.Submit) } ) } diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenterTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenterTest.kt index a6ffb57dc4..a1c7d697d6 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenterTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenterTest.kt @@ -56,7 +56,7 @@ class SecureBackupEnterRecoveryKeyPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(SecureBackupEnterRecoveryKeyEvents.OnRecoveryKeyChange("1234")) + initialState.eventSink(SecureBackupEnterRecoveryKeyEvent.OnRecoveryKeyChange("1234")) val withRecoveryKeyState = awaitItem() assertThat(withRecoveryKeyState.isSubmitEnabled).isTrue() assertThat(withRecoveryKeyState.recoveryKeyViewState).isEqualTo( @@ -68,19 +68,19 @@ class SecureBackupEnterRecoveryKeyPresenterTest { ) ) encryptionService.givenRecoverFailure(AN_EXCEPTION) - withRecoveryKeyState.eventSink(SecureBackupEnterRecoveryKeyEvents.Submit) + withRecoveryKeyState.eventSink(SecureBackupEnterRecoveryKeyEvent.Submit) val loadingState = awaitItem() assertThat(loadingState.submitAction).isEqualTo(AsyncAction.Loading) assertThat(loadingState.isSubmitEnabled).isFalse() val errorState = awaitItem() assertThat(errorState.submitAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) assertThat(errorState.isSubmitEnabled).isFalse() - errorState.eventSink(SecureBackupEnterRecoveryKeyEvents.ClearDialog) + errorState.eventSink(SecureBackupEnterRecoveryKeyEvent.ClearDialog) val clearedState = awaitItem() assertThat(clearedState.submitAction).isEqualTo(AsyncAction.Uninitialized) assertThat(clearedState.isSubmitEnabled).isTrue() encryptionService.givenRecoverFailure(null) - clearedState.eventSink(SecureBackupEnterRecoveryKeyEvents.Submit) + clearedState.eventSink(SecureBackupEnterRecoveryKeyEvent.Submit) val loadingState2 = awaitItem() assertThat(loadingState2.submitAction).isEqualTo(AsyncAction.Loading) assertThat(loadingState2.isSubmitEnabled).isFalse() diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyViewTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyViewTest.kt index fcf050f925..5bc880a685 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyViewTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyViewTest.kt @@ -58,32 +58,32 @@ class SecureBackupEnterRecoveryKeyViewTest : RobolectricTest() { @Test @Config(qualifiers = "h1024dp") fun `tapping on Continue when key is valid - calls expected action`() = runAndroidComposeUiTest { - val recorder = EventsRecorder() + val recorder = EventsRecorder() setSecureBackupEnterRecoveryKeyView( aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder), ) clickOn(CommonStrings.action_continue) - recorder.assertSingle(SecureBackupEnterRecoveryKeyEvents.Submit) + recorder.assertSingle(SecureBackupEnterRecoveryKeyEvent.Submit) } @Test fun `entering a char emits the expected event`() = runAndroidComposeUiTest { - val recorder = EventsRecorder() + val recorder = EventsRecorder() val keyValue = aFormattedRecoveryKey() setSecureBackupEnterRecoveryKeyView( aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder), ) onNodeWithText(keyValue).performTextInput("X") recorder.assertSingle( - SecureBackupEnterRecoveryKeyEvents.OnRecoveryKeyChange("X$keyValue") + SecureBackupEnterRecoveryKeyEvent.OnRecoveryKeyChange("X$keyValue") ) } @Test @Config(qualifiers = "h1024dp") fun `toggling the visibility of the textfield changes it`() = runAndroidComposeUiTest { - val recorder = EventsRecorder() + val recorder = EventsRecorder() val keyValue = aFormattedRecoveryKey() setSecureBackupEnterRecoveryKeyView(aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder)) @@ -94,18 +94,18 @@ class SecureBackupEnterRecoveryKeyViewTest : RobolectricTest() { waitForIdle() - recorder.assertSingle(SecureBackupEnterRecoveryKeyEvents.ChangeRecoveryKeyFieldContentsVisibility(false)) + recorder.assertSingle(SecureBackupEnterRecoveryKeyEvent.ChangeRecoveryKeyFieldContentsVisibility(false)) } @Test fun `validating from keyboard emits the expected event`() = runAndroidComposeUiTest { - val recorder = EventsRecorder() + val recorder = EventsRecorder() val keyValue = aFormattedRecoveryKey() setSecureBackupEnterRecoveryKeyView( aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder), ) onNodeWithText(keyValue).performImeAction() - recorder.assertSingle(SecureBackupEnterRecoveryKeyEvents.Submit) + recorder.assertSingle(SecureBackupEnterRecoveryKeyEvent.Submit) } @Test From 28c19e38ce9aa6997c107c5dbbd5673b3ac9ddde Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:21:36 +0200 Subject: [PATCH 054/112] `SecureBackupRootEvents` -> `SecureBackupRootEvent` --- ...ackupRootEvents.kt => SecureBackupRootEvent.kt} | 10 +++++----- .../impl/root/SecureBackupRootPresenter.kt | 10 +++++----- .../impl/root/SecureBackupRootState.kt | 2 +- .../securebackup/impl/root/SecureBackupRootView.kt | 14 +++++++------- .../impl/root/SecureBackupRootPresenterTest.kt | 8 ++++---- 5 files changed, 22 insertions(+), 22 deletions(-) rename features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/{SecureBackupRootEvents.kt => SecureBackupRootEvent.kt} (62%) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootEvents.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootEvent.kt similarity index 62% rename from features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootEvents.kt rename to features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootEvent.kt index 9d371e3c30..91bcf67575 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootEvents.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootEvent.kt @@ -8,9 +8,9 @@ package io.element.android.features.securebackup.impl.root -sealed interface SecureBackupRootEvents { - data object RetryKeyBackupState : SecureBackupRootEvents - data object EnableKeyStorage : SecureBackupRootEvents - data object DisplayKeyStorageDisabledError : SecureBackupRootEvents - data object DismissDialog : SecureBackupRootEvents +sealed interface SecureBackupRootEvent { + data object RetryKeyBackupState : SecureBackupRootEvent + data object EnableKeyStorage : SecureBackupRootEvent + data object DisplayKeyStorageDisabledError : SecureBackupRootEvent + data object DismissDialog : SecureBackupRootEvent } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt index 9a97bfac25..602fd773da 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt @@ -59,15 +59,15 @@ class SecureBackupRootPresenter( } } - fun handleEvent(event: SecureBackupRootEvents) { + fun handleEvent(event: SecureBackupRootEvent) { when (event) { - SecureBackupRootEvents.RetryKeyBackupState -> localCoroutineScope.getKeyBackupStatus(doesBackupExistOnServerAction) - SecureBackupRootEvents.EnableKeyStorage -> localCoroutineScope.enableBackup(enableAction) - SecureBackupRootEvents.DismissDialog -> { + SecureBackupRootEvent.RetryKeyBackupState -> localCoroutineScope.getKeyBackupStatus(doesBackupExistOnServerAction) + SecureBackupRootEvent.EnableKeyStorage -> localCoroutineScope.enableBackup(enableAction) + SecureBackupRootEvent.DismissDialog -> { enableAction.value = AsyncAction.Uninitialized displayKeyStorageDisabledError = false } - SecureBackupRootEvents.DisplayKeyStorageDisabledError -> displayKeyStorageDisabledError = true + SecureBackupRootEvent.DisplayKeyStorageDisabledError -> displayKeyStorageDisabledError = true } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootState.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootState.kt index 1cbf917ca5..f528910fe2 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootState.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootState.kt @@ -22,7 +22,7 @@ data class SecureBackupRootState( val appName: String, val displayKeyStorageDisabledError: Boolean, val snackbarMessage: SnackbarMessage?, - val eventSink: (SecureBackupRootEvents) -> Unit, + val eventSink: (SecureBackupRootEvent) -> Unit, ) { val isKeyStorageEnabled: Boolean get() = when (backupState) { diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt index 8059329cd0..fb45228ce4 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt @@ -114,12 +114,12 @@ fun SecureBackupRootView( if (state.doesBackupExistOnServer.data) { onDisableClick() } else { - state.eventSink.invoke(SecureBackupRootEvents.EnableKeyStorage) + state.eventSink.invoke(SecureBackupRootEvent.EnableKeyStorage) } } is AsyncData.Loading, AsyncData.Uninitialized -> Unit - is AsyncData.Failure -> state.eventSink.invoke(SecureBackupRootEvents.RetryKeyBackupState) + is AsyncData.Failure -> state.eventSink.invoke(SecureBackupRootEvent.RetryKeyBackupState) } } BackupState.CREATING, @@ -154,7 +154,7 @@ fun SecureBackupRootView( if (state.isKeyStorageEnabled) { onSetupClick() } else { - state.eventSink.invoke(SecureBackupRootEvents.DisplayKeyStorageDisabledError) + state.eventSink.invoke(SecureBackupRootEvent.DisplayKeyStorageDisabledError) } }, ) @@ -177,7 +177,7 @@ fun SecureBackupRootView( if (state.isKeyStorageEnabled) { onChangeClick() } else { - state.eventSink.invoke(SecureBackupRootEvents.DisplayKeyStorageDisabledError) + state.eventSink.invoke(SecureBackupRootEvent.DisplayKeyStorageDisabledError) } }, ) @@ -201,7 +201,7 @@ fun SecureBackupRootView( if (state.isKeyStorageEnabled) { onConfirmRecoveryKeyClick() } else { - state.eventSink.invoke(SecureBackupRootEvents.DisplayKeyStorageDisabledError) + state.eventSink.invoke(SecureBackupRootEvent.DisplayKeyStorageDisabledError) } }, ) @@ -212,13 +212,13 @@ fun SecureBackupRootView( async = state.enableAction, progressDialog = { }, onSuccess = { }, - onErrorDismiss = { state.eventSink.invoke(SecureBackupRootEvents.DismissDialog) } + onErrorDismiss = { state.eventSink.invoke(SecureBackupRootEvent.DismissDialog) } ) if (state.displayKeyStorageDisabledError) { ErrorDialog( title = null, content = stringResource(id = R.string.screen_chat_backup_key_storage_disabled_error), - onSubmit = { state.eventSink.invoke(SecureBackupRootEvents.DismissDialog) }, + onSubmit = { state.eventSink.invoke(SecureBackupRootEvent.DismissDialog) }, ) } } diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenterTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenterTest.kt index 63adfc789e..bbee28803e 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenterTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenterTest.kt @@ -66,7 +66,7 @@ class SecureBackupRootPresenterTest { val errorState = awaitItem() assertThat(errorState.doesBackupExistOnServer).isEqualTo(AsyncData.Failure(AN_EXCEPTION)) encryptionService.givenDoesBackupExistOnServerResult(Result.success(false)) - errorState.eventSink.invoke(SecureBackupRootEvents.RetryKeyBackupState) + errorState.eventSink.invoke(SecureBackupRootEvent.RetryKeyBackupState) val loadingState2 = awaitItem() assertThat(loadingState2.doesBackupExistOnServer).isInstanceOf(AsyncData.Loading::class.java) val finalState = awaitItem() @@ -82,9 +82,9 @@ class SecureBackupRootPresenterTest { }.test { skipItems(2) val initialState = awaitItem() - initialState.eventSink(SecureBackupRootEvents.DisplayKeyStorageDisabledError) + initialState.eventSink(SecureBackupRootEvent.DisplayKeyStorageDisabledError) assertThat(awaitItem().displayKeyStorageDisabledError).isTrue() - initialState.eventSink(SecureBackupRootEvents.DismissDialog) + initialState.eventSink(SecureBackupRootEvent.DismissDialog) assertThat(awaitItem().displayKeyStorageDisabledError).isFalse() } } @@ -97,7 +97,7 @@ class SecureBackupRootPresenterTest { }.test { skipItems(2) val initialState = awaitItem() - initialState.eventSink(SecureBackupRootEvents.EnableKeyStorage) + initialState.eventSink(SecureBackupRootEvent.EnableKeyStorage) assertThat(awaitItem().enableAction.isLoading()).isTrue() assertThat(awaitItem().enableAction.isSuccess()).isTrue() } From b8b6dc1df6fff96baf9304112930df4271edb46a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:22:22 +0200 Subject: [PATCH 055/112] `SecureBackupSetupEvents` -> `SecureBackupSetupEvent` --- ...tupEvents.kt => SecureBackupSetupEvent.kt} | 10 +++++----- .../impl/setup/SecureBackupSetupPresenter.kt | 10 +++++----- .../impl/setup/SecureBackupSetupState.kt | 2 +- .../impl/setup/SecureBackupSetupView.kt | 12 +++++------ .../setup/SecureBackupSetupPresenterTest.kt | 20 +++++++++---------- 5 files changed, 27 insertions(+), 27 deletions(-) rename features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/{SecureBackupSetupEvents.kt => SecureBackupSetupEvent.kt} (63%) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupEvents.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupEvent.kt similarity index 63% rename from features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupEvents.kt rename to features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupEvent.kt index f61e65ba61..68221a29e4 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupEvents.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupEvent.kt @@ -8,9 +8,9 @@ package io.element.android.features.securebackup.impl.setup -sealed interface SecureBackupSetupEvents { - data object CreateRecoveryKey : SecureBackupSetupEvents - data object RecoveryKeyHasBeenSaved : SecureBackupSetupEvents - data object Done : SecureBackupSetupEvents - data object DismissDialog : SecureBackupSetupEvents +sealed interface SecureBackupSetupEvent { + data object CreateRecoveryKey : SecureBackupSetupEvent + data object RecoveryKeyHasBeenSaved : SecureBackupSetupEvent + data object Done : SecureBackupSetupEvent + data object DismissDialog : SecureBackupSetupEvent } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt index 9f27bc9566..538128a0d5 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt @@ -53,18 +53,18 @@ class SecureBackupSetupPresenter( } var showSaveConfirmationDialog by remember { mutableStateOf(false) } - fun handleEvent(event: SecureBackupSetupEvents) { + fun handleEvent(event: SecureBackupSetupEvent) { when (event) { - SecureBackupSetupEvents.CreateRecoveryKey -> { + SecureBackupSetupEvent.CreateRecoveryKey -> { coroutineScope.createOrChangeRecoveryKey(stateAndDispatch) } - SecureBackupSetupEvents.RecoveryKeyHasBeenSaved -> + SecureBackupSetupEvent.RecoveryKeyHasBeenSaved -> stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.UserSavedKey) - SecureBackupSetupEvents.DismissDialog -> { + SecureBackupSetupEvent.DismissDialog -> { showSaveConfirmationDialog = false stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.ClearError) } - SecureBackupSetupEvents.Done -> { + SecureBackupSetupEvent.Done -> { showSaveConfirmationDialog = true } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupState.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupState.kt index 752b5e4851..ecec6e3f6a 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupState.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupState.kt @@ -15,7 +15,7 @@ data class SecureBackupSetupState( val recoveryKeyViewState: RecoveryKeyViewState, val showSaveConfirmationDialog: Boolean, val setupState: SetupState, - val eventSink: (SecureBackupSetupEvents) -> Unit + val eventSink: (SecureBackupSetupEvent) -> Unit ) sealed interface SetupState { diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt index cab1e0995c..c22bd44c99 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt @@ -56,7 +56,7 @@ fun SecureBackupSetupView( title = stringResource(id = CommonStrings.common_something_went_wrong), content = stringResource(id = CommonStrings.common_something_went_wrong_message), onSubmit = { - state.eventSink.invoke(SecureBackupSetupEvents.DismissDialog) + state.eventSink.invoke(SecureBackupSetupEvent.DismissDialog) }, ) } @@ -68,7 +68,7 @@ fun SecureBackupSetupView( submitText = stringResource(id = CommonStrings.action_continue), onSubmitClick = onSuccess, onDismiss = { - state.eventSink.invoke(SecureBackupSetupEvents.DismissDialog) + state.eventSink.invoke(SecureBackupSetupEvent.DismissDialog) } ) } @@ -123,12 +123,12 @@ private fun Content( text = formattedRecoveryKey, toastMessage = toastMessage, ) - state.eventSink.invoke(SecureBackupSetupEvents.RecoveryKeyHasBeenSaved) + state.eventSink.invoke(SecureBackupSetupEvent.RecoveryKeyHasBeenSaved) } } else { if (!state.recoveryKeyViewState.inProgress) { { - state.eventSink.invoke(SecureBackupSetupEvents.CreateRecoveryKey) + state.eventSink.invoke(SecureBackupSetupEvent.CreateRecoveryKey) } } else { null @@ -174,7 +174,7 @@ private fun ColumnScope.Buttons( chooserTitle = chooserTitle, text = state.setupState.recoveryKey()!!, ) - state.eventSink.invoke(SecureBackupSetupEvents.RecoveryKeyHasBeenSaved) + state.eventSink.invoke(SecureBackupSetupEvent.RecoveryKeyHasBeenSaved) }, ) Button( @@ -184,7 +184,7 @@ private fun ColumnScope.Buttons( if (state.setupState is SetupState.CreatedAndSaved) { onFinish() } else { - state.eventSink.invoke(SecureBackupSetupEvents.Done) + state.eventSink.invoke(SecureBackupSetupEvent.Done) } }, ) diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenterTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenterTest.kt index 4aede3c534..6ab0b257a2 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenterTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenterTest.kt @@ -58,7 +58,7 @@ class SecureBackupSetupPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink.invoke(SecureBackupSetupEvents.CreateRecoveryKey) + initialState.eventSink.invoke(SecureBackupSetupEvent.CreateRecoveryKey) val creatingState = awaitItem() assertThat(creatingState.setupState).isEqualTo(SetupState.Creating) assertThat(creatingState.recoveryKeyViewState).isEqualTo( @@ -80,13 +80,13 @@ class SecureBackupSetupPresenterTest { inProgress = false, ) ) - createdState.eventSink.invoke(SecureBackupSetupEvents.RecoveryKeyHasBeenSaved) + createdState.eventSink.invoke(SecureBackupSetupEvent.RecoveryKeyHasBeenSaved) val createdAndSaveState = awaitItem() assertThat(createdAndSaveState.setupState).isInstanceOf(SetupState.CreatedAndSaved::class.java) - createdAndSaveState.eventSink.invoke(SecureBackupSetupEvents.Done) + createdAndSaveState.eventSink.invoke(SecureBackupSetupEvent.Done) val doneState = awaitItem() assertThat(doneState.showSaveConfirmationDialog).isTrue() - doneState.eventSink.invoke(SecureBackupSetupEvents.DismissDialog) + doneState.eventSink.invoke(SecureBackupSetupEvent.DismissDialog) val doneStateCancelled = awaitItem() assertThat(doneStateCancelled.showSaveConfirmationDialog).isFalse() } @@ -130,12 +130,12 @@ class SecureBackupSetupPresenterTest { assertThat(initialState.isChangeRecoveryKeyUserStory).isFalse() assertThat(initialState.setupState).isEqualTo(SetupState.Init) - initialState.eventSink(SecureBackupSetupEvents.CreateRecoveryKey) + initialState.eventSink(SecureBackupSetupEvent.CreateRecoveryKey) val creatingState = awaitItem() assertThat(creatingState.setupState).isEqualTo(SetupState.Creating) val failedState = awaitItem() assertThat(failedState.setupState).isInstanceOf(SetupState.Error::class.java) - failedState.eventSink(SecureBackupSetupEvents.DismissDialog) + failedState.eventSink(SecureBackupSetupEvent.DismissDialog) val finalState = awaitItem() assertThat(finalState.setupState).isEqualTo(SetupState.Init) @@ -153,7 +153,7 @@ class SecureBackupSetupPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink.invoke(SecureBackupSetupEvents.CreateRecoveryKey) + initialState.eventSink.invoke(SecureBackupSetupEvent.CreateRecoveryKey) val creatingState = awaitItem() assertThat(creatingState.setupState).isEqualTo(SetupState.Creating) assertThat(creatingState.recoveryKeyViewState).isEqualTo( @@ -174,13 +174,13 @@ class SecureBackupSetupPresenterTest { inProgress = false, ) ) - createdState.eventSink.invoke(SecureBackupSetupEvents.RecoveryKeyHasBeenSaved) + createdState.eventSink.invoke(SecureBackupSetupEvent.RecoveryKeyHasBeenSaved) val createdAndSaveState = awaitItem() assertThat(createdAndSaveState.setupState).isInstanceOf(SetupState.CreatedAndSaved::class.java) - createdAndSaveState.eventSink.invoke(SecureBackupSetupEvents.Done) + createdAndSaveState.eventSink.invoke(SecureBackupSetupEvent.Done) val doneState = awaitItem() assertThat(doneState.showSaveConfirmationDialog).isTrue() - doneState.eventSink.invoke(SecureBackupSetupEvents.DismissDialog) + doneState.eventSink.invoke(SecureBackupSetupEvent.DismissDialog) val doneStateCancelled = awaitItem() assertThat(doneStateCancelled.showSaveConfirmationDialog).isFalse() } From 47f4e136ddbe0835316fded54447843590f04e87 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:22:31 +0200 Subject: [PATCH 056/112] Use fontHeadingMdBold for empty space title. --- .../element/android/features/home/impl/spaces/HomeSpacesView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesView.kt index 914d08d7f6..f9cf38a1aa 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesView.kt @@ -143,7 +143,7 @@ private fun EmptySpaceHomeView( ) Text( text = stringResource(CommonStrings.screen_space_list_empty_state_title), - style = ElementTheme.typography.fontHeadingLgBold, + style = ElementTheme.typography.fontHeadingMdBold, color = ElementTheme.colors.textPrimary, textAlign = TextAlign.Center, ) From deb08a3a6c3afc1584d551164eed6fe70148c063 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:23:12 +0200 Subject: [PATCH 057/112] `EditRoomAddressEvents` -> `EditRoomAddressEvent` --- ...dressEvents.kt => EditRoomAddressEvent.kt} | 8 +++--- .../EditRoomAddressPresenter.kt | 8 +++--- .../editroomaddress/EditRoomAddressState.kt | 2 +- .../EditRoomAddressStatePreviewParam.kt | 2 +- .../editroomaddress/EditRoomAddressView.kt | 8 +++--- .../EditRoomAddressPresenterTest.kt | 26 +++++++++---------- .../EditRoomAddressViewTest.kt | 18 ++++++------- 7 files changed, 36 insertions(+), 36 deletions(-) rename features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/{EditRoomAddressEvents.kt => EditRoomAddressEvent.kt} (70%) diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressEvents.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressEvent.kt similarity index 70% rename from features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressEvents.kt rename to features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressEvent.kt index e42236d31c..e9cac38806 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressEvents.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressEvent.kt @@ -8,8 +8,8 @@ package io.element.android.features.securityandprivacy.impl.editroomaddress -sealed interface EditRoomAddressEvents { - data object Save : EditRoomAddressEvents - data object DismissError : EditRoomAddressEvents - data class RoomAddressChanged(val roomAddress: String) : EditRoomAddressEvents +sealed interface EditRoomAddressEvent { + data object Save : EditRoomAddressEvent + data object DismissError : EditRoomAddressEvent + data class RoomAddressChanged(val roomAddress: String) : EditRoomAddressEvent } diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressPresenter.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressPresenter.kt index 8dcea0d880..df4c4a88e1 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressPresenter.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressPresenter.kt @@ -63,17 +63,17 @@ class EditRoomAddressPresenter( ) } - fun handleEvent(event: EditRoomAddressEvents) { + fun handleEvent(event: EditRoomAddressEvent) { when (event) { - EditRoomAddressEvents.Save -> coroutineScope.save( + EditRoomAddressEvent.Save -> coroutineScope.save( saveAction = saveAction, serverName = homeserverName, newRoomAddress = newRoomAddress ) - is EditRoomAddressEvents.RoomAddressChanged -> { + is EditRoomAddressEvent.RoomAddressChanged -> { newRoomAddress = event.roomAddress } - EditRoomAddressEvents.DismissError -> { + EditRoomAddressEvent.DismissError -> { saveAction.value = AsyncAction.Uninitialized } } diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressState.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressState.kt index 89315ab8ce..a9a4b9c669 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressState.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressState.kt @@ -16,7 +16,7 @@ data class EditRoomAddressState( val roomAddress: String, val roomAddressValidity: RoomAddressValidity, val saveAction: AsyncAction, - val eventSink: (EditRoomAddressEvents) -> Unit + val eventSink: (EditRoomAddressEvent) -> Unit ) { val canBeSaved = roomAddressValidity == RoomAddressValidity.Valid } diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressStatePreviewParam.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressStatePreviewParam.kt index 096e9c50c5..577bfc9957 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressStatePreviewParam.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressStatePreviewParam.kt @@ -28,7 +28,7 @@ fun anEditRoomAddressState( roomAddressValidity: RoomAddressValidity = RoomAddressValidity.Unknown, homeserverName: String = ":myserver.org", saveAction: AsyncAction = AsyncAction.Uninitialized, - eventSink: (EditRoomAddressEvents) -> Unit = {} + eventSink: (EditRoomAddressEvent) -> Unit = {} ) = EditRoomAddressState( roomAddress = roomAddress, roomAddressValidity = roomAddressValidity, diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressView.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressView.kt index c1156df209..9eb15b7af9 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressView.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressView.kt @@ -46,7 +46,7 @@ fun EditRoomAddressView( isSaveActionEnabled = state.canBeSaved, onBackClick = onBackClick, onSaveClick = { - state.eventSink(EditRoomAddressEvents.Save) + state.eventSink(EditRoomAddressEvent.Save) }, ) } @@ -63,7 +63,7 @@ fun EditRoomAddressView( homeserverName = state.homeserverName, addressValidity = state.roomAddressValidity, onAddressChange = { - state.eventSink(EditRoomAddressEvents.RoomAddressChanged(it)) + state.eventSink(EditRoomAddressEvent.RoomAddressChanged(it)) }, label = stringResource(R.string.screen_edit_room_address_title), supportingText = stringResource(R.string.screen_edit_room_address_room_address_section_footer), @@ -81,8 +81,8 @@ fun EditRoomAddressView( }, onSuccess = {}, errorMessage = { stringResource(CommonStrings.error_unknown) }, - onRetry = { state.eventSink(EditRoomAddressEvents.Save) }, - onErrorDismiss = { state.eventSink(EditRoomAddressEvents.DismissError) }, + onRetry = { state.eventSink(EditRoomAddressEvent.Save) }, + onErrorDismiss = { state.eventSink(EditRoomAddressEvent.DismissError) }, ) } } diff --git a/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressPresenterTest.kt b/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressPresenterTest.kt index aac25ec018..9d2b7f9228 100644 --- a/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressPresenterTest.kt +++ b/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressPresenterTest.kt @@ -97,7 +97,7 @@ class EditRoomAddressPresenterTest { val presenter = createEditRoomAddressPresenter(roomAliasHelper = roomAliasHelper) presenter.test { with(awaitItem()) { - eventSink(EditRoomAddressEvents.RoomAddressChanged("invalid")) + eventSink(EditRoomAddressEvent.RoomAddressChanged("invalid")) } with(awaitItem()) { assertThat(roomAddress).isEqualTo("invalid") @@ -115,7 +115,7 @@ class EditRoomAddressPresenterTest { val presenter = createEditRoomAddressPresenter() presenter.test { with(awaitItem()) { - eventSink(EditRoomAddressEvents.RoomAddressChanged("valid")) + eventSink(EditRoomAddressEvent.RoomAddressChanged("valid")) } with(awaitItem()) { assertThat(roomAddress).isEqualTo("valid") @@ -134,7 +134,7 @@ class EditRoomAddressPresenterTest { val presenter = createEditRoomAddressPresenter(client = client) presenter.test { with(awaitItem()) { - eventSink(EditRoomAddressEvents.RoomAddressChanged("valid")) + eventSink(EditRoomAddressEvent.RoomAddressChanged("valid")) } with(awaitItem()) { assertThat(roomAddress).isEqualTo("valid") @@ -163,13 +163,13 @@ class EditRoomAddressPresenterTest { val presenter = createEditRoomAddressPresenter(room = room, navigator = navigator) presenter.test { with(awaitItem()) { - eventSink(EditRoomAddressEvents.RoomAddressChanged("valid")) + eventSink(EditRoomAddressEvent.RoomAddressChanged("valid")) } skipItems(1) with(awaitItem()) { assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.Valid) assertThat(canBeSaved).isTrue() - eventSink(EditRoomAddressEvents.Save) + eventSink(EditRoomAddressEvent.Save) } with(awaitItem()) { assertThat(saveAction).isEqualTo(AsyncAction.Loading) @@ -212,13 +212,13 @@ class EditRoomAddressPresenterTest { val presenter = createEditRoomAddressPresenter(room = room, navigator = navigator) presenter.test { with(awaitItem()) { - eventSink(EditRoomAddressEvents.RoomAddressChanged("valid")) + eventSink(EditRoomAddressEvent.RoomAddressChanged("valid")) } skipItems(1) with(awaitItem()) { assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.Valid) assertThat(canBeSaved).isTrue() - eventSink(EditRoomAddressEvents.Save) + eventSink(EditRoomAddressEvent.Save) } with(awaitItem()) { assertThat(saveAction).isEqualTo(AsyncAction.Loading) @@ -263,13 +263,13 @@ class EditRoomAddressPresenterTest { val presenter = createEditRoomAddressPresenter(room = room, navigator = navigator) presenter.test { with(awaitItem()) { - eventSink(EditRoomAddressEvents.RoomAddressChanged("valid")) + eventSink(EditRoomAddressEvent.RoomAddressChanged("valid")) } skipItems(1) with(awaitItem()) { assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.Valid) assertThat(canBeSaved).isTrue() - eventSink(EditRoomAddressEvents.Save) + eventSink(EditRoomAddressEvent.Save) } with(awaitItem()) { assertThat(saveAction).isEqualTo(AsyncAction.Loading) @@ -309,13 +309,13 @@ class EditRoomAddressPresenterTest { ) presenter.test { with(awaitItem()) { - eventSink(EditRoomAddressEvents.RoomAddressChanged("valid")) + eventSink(EditRoomAddressEvent.RoomAddressChanged("valid")) } skipItems(1) with(awaitItem()) { assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.Valid) assertThat(canBeSaved).isTrue() - eventSink(EditRoomAddressEvents.Save) + eventSink(EditRoomAddressEvent.Save) } with(awaitItem()) { assertThat(saveAction).isEqualTo(AsyncAction.Loading) @@ -339,12 +339,12 @@ class EditRoomAddressPresenterTest { ) presenter.test { with(awaitItem()) { - eventSink(EditRoomAddressEvents.Save) + eventSink(EditRoomAddressEvent.Save) } assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Loading::class.java) with(awaitItem()) { assertThat(saveAction).isInstanceOf(AsyncAction.Failure::class.java) - eventSink(EditRoomAddressEvents.DismissError) + eventSink(EditRoomAddressEvent.DismissError) } with(awaitItem()) { assertThat(saveAction).isEqualTo(AsyncAction.Uninitialized) diff --git a/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressViewTest.kt b/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressViewTest.kt index 1d0886f52a..beeab1cd14 100644 --- a/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressViewTest.kt +++ b/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressViewTest.kt @@ -39,7 +39,7 @@ class EditRoomAddressViewTest : RobolectricTest() { @Test fun `click on disabled save doesn't emit event`() = runAndroidComposeUiTest { - val recorder = EventsRecorder(expectEvents = false) + val recorder = EventsRecorder(expectEvents = false) val state = anEditRoomAddressState(eventSink = recorder) setEditRoomAddressView(state) clickOn(CommonStrings.action_save) @@ -48,7 +48,7 @@ class EditRoomAddressViewTest : RobolectricTest() { @Test fun `click on enabled save emits the expected event`() = runAndroidComposeUiTest { - val recorder = EventsRecorder() + val recorder = EventsRecorder() val state = anEditRoomAddressState( roomAddress = "room", roomAddressValidity = RoomAddressValidity.Valid, @@ -56,12 +56,12 @@ class EditRoomAddressViewTest : RobolectricTest() { ) setEditRoomAddressView(state) clickOn(CommonStrings.action_save) - recorder.assertSingle(EditRoomAddressEvents.Save) + recorder.assertSingle(EditRoomAddressEvent.Save) } @Test fun `text changes on text field emits the expected event`() = runAndroidComposeUiTest { - val recorder = EventsRecorder() + val recorder = EventsRecorder() val state = anEditRoomAddressState( roomAddress = "", eventSink = recorder @@ -69,12 +69,12 @@ class EditRoomAddressViewTest : RobolectricTest() { setEditRoomAddressView(state) onNodeWithTag(TestTags.roomAddressField.value).performTextInput("alias") - recorder.assertSingle(EditRoomAddressEvents.RoomAddressChanged("alias")) + recorder.assertSingle(EditRoomAddressEvent.RoomAddressChanged("alias")) } @Test fun `click on dismiss error emits the expected event`() = runAndroidComposeUiTest { - val recorder = EventsRecorder() + val recorder = EventsRecorder() val state = anEditRoomAddressState( roomAddress = "", saveAction = AsyncAction.Failure(IllegalStateException()), @@ -82,12 +82,12 @@ class EditRoomAddressViewTest : RobolectricTest() { ) setEditRoomAddressView(state) clickOn(CommonStrings.action_cancel) - recorder.assertSingle(EditRoomAddressEvents.DismissError) + recorder.assertSingle(EditRoomAddressEvent.DismissError) } @Test fun `click on retry error emits the expected event`() = runAndroidComposeUiTest { - val recorder = EventsRecorder() + val recorder = EventsRecorder() val state = anEditRoomAddressState( roomAddress = "", saveAction = AsyncAction.Failure(IllegalStateException()), @@ -95,7 +95,7 @@ class EditRoomAddressViewTest : RobolectricTest() { ) setEditRoomAddressView(state) clickOn(CommonStrings.action_retry) - recorder.assertSingle(EditRoomAddressEvents.Save) + recorder.assertSingle(EditRoomAddressEvent.Save) } } From 739fc452dd1fbcb32698a95ad5a8d2a545716f66 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:23:51 +0200 Subject: [PATCH 058/112] `ShareEvents` -> `ShareEvent` --- .../features/share/impl/{ShareEvents.kt => ShareEvent.kt} | 4 ++-- .../io/element/android/features/share/impl/SharePresenter.kt | 4 ++-- .../io/element/android/features/share/impl/ShareState.kt | 2 +- .../android/features/share/impl/ShareStatePreviewParam.kt | 2 +- .../io/element/android/features/share/impl/ShareView.kt | 2 +- .../element/android/features/share/impl/SharePresenterTest.kt | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) rename features/share/impl/src/main/kotlin/io/element/android/features/share/impl/{ShareEvents.kt => ShareEvent.kt} (80%) diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareEvents.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareEvent.kt similarity index 80% rename from features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareEvents.kt rename to features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareEvent.kt index d0e246fe44..5137c50f2d 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareEvents.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareEvent.kt @@ -8,6 +8,6 @@ package io.element.android.features.share.impl -sealed interface ShareEvents { - data object ClearError : ShareEvents +sealed interface ShareEvent { + data object ClearError : ShareEvent } diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/SharePresenter.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/SharePresenter.kt index 93a9ae3bf4..3bd9b98227 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/SharePresenter.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/SharePresenter.kt @@ -55,9 +55,9 @@ class SharePresenter( @Composable override fun present(): ShareState { - fun handleEvent(event: ShareEvents) { + fun handleEvent(event: ShareEvent) { when (event) { - ShareEvents.ClearError -> shareActionState.value = AsyncAction.Uninitialized + ShareEvent.ClearError -> shareActionState.value = AsyncAction.Uninitialized } } diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareState.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareState.kt index fd98d58ba3..7538309941 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareState.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareState.kt @@ -13,5 +13,5 @@ import io.element.android.libraries.matrix.api.core.RoomId data class ShareState( val shareAction: AsyncAction>, - val eventSink: (ShareEvents) -> Unit + val eventSink: (ShareEvent) -> Unit ) diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareStatePreviewParam.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareStatePreviewParam.kt index 97135acb71..18258f1b95 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareStatePreviewParam.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareStatePreviewParam.kt @@ -32,7 +32,7 @@ open class ShareStatePreviewParam : PreviewParameterProvider { fun aShareState( shareAction: AsyncAction> = AsyncAction.Uninitialized, - eventSink: (ShareEvents) -> Unit = {} + eventSink: (ShareEvent) -> Unit = {} ) = ShareState( shareAction = shareAction, eventSink = eventSink diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareView.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareView.kt index 5e5130965f..2516c31a66 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareView.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareView.kt @@ -26,7 +26,7 @@ fun ShareView( onShareSuccess(it) }, onErrorDismiss = { - state.eventSink(ShareEvents.ClearError) + state.eventSink(ShareEvent.ClearError) }, ) } diff --git a/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/SharePresenterTest.kt b/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/SharePresenterTest.kt index ab3a260c01..44ccea92d6 100644 --- a/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/SharePresenterTest.kt +++ b/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/SharePresenterTest.kt @@ -65,7 +65,7 @@ class SharePresenterTest : RobolectricTest() { assertThat(awaitItem().shareAction.isLoading()).isTrue() val failure = awaitItem() assertThat(failure.shareAction.isFailure()).isTrue() - failure.eventSink.invoke(ShareEvents.ClearError) + failure.eventSink.invoke(ShareEvent.ClearError) assertThat(awaitItem().shareAction.isUninitialized()).isTrue() } } From a2c3ddab02f60382ff89a265c79140ebb6e1e2e1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:24:37 +0200 Subject: [PATCH 059/112] `SignedOutEvents` -> `SignedOutEvent` --- .../signedout/impl/{SignedOutEvents.kt => SignedOutEvent.kt} | 4 ++-- .../android/features/signedout/impl/SignedOutPresenter.kt | 4 ++-- .../element/android/features/signedout/impl/SignedOutState.kt | 2 +- .../element/android/features/signedout/impl/SignedOutView.kt | 4 ++-- .../android/features/signedout/impl/SignedOutPresenterTest.kt | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) rename features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/{SignedOutEvents.kt => SignedOutEvent.kt} (78%) diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutEvents.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutEvent.kt similarity index 78% rename from features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutEvents.kt rename to features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutEvent.kt index 3b78af9918..1e774488e5 100644 --- a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutEvents.kt +++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutEvent.kt @@ -8,6 +8,6 @@ package io.element.android.features.signedout.impl -sealed interface SignedOutEvents { - data object SignInAgain : SignedOutEvents +sealed interface SignedOutEvent { + data object SignInAgain : SignedOutEvent } diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutPresenter.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutPresenter.kt index 1aa13d9b8d..1f1a9be1d7 100644 --- a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutPresenter.kt +++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutPresenter.kt @@ -43,9 +43,9 @@ class SignedOutPresenter( }.collectAsState(initial = null) val coroutineScope = rememberCoroutineScope() - fun handleEvent(event: SignedOutEvents) { + fun handleEvent(event: SignedOutEvent) { when (event) { - SignedOutEvents.SignInAgain -> coroutineScope.launch { + SignedOutEvent.SignInAgain -> coroutineScope.launch { sessionStore.removeSession(sessionId.value) } } diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutState.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutState.kt index bc1ee18f5a..aeff7c7d2f 100644 --- a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutState.kt +++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutState.kt @@ -13,5 +13,5 @@ import io.element.android.libraries.sessionstorage.api.SessionData data class SignedOutState( val appName: String, val signedOutSession: SessionData?, - val eventSink: (SignedOutEvents) -> Unit, + val eventSink: (SignedOutEvent) -> Unit, ) diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt index 9dcb4585e5..73e854c836 100644 --- a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt +++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt @@ -39,7 +39,7 @@ fun SignedOutView( state: SignedOutState, modifier: Modifier = Modifier, ) { - BackHandler(onBack = { state.eventSink(SignedOutEvents.SignInAgain) }) + BackHandler(onBack = { state.eventSink(SignedOutEvent.SignInAgain) }) HeaderFooterPage( modifier = modifier .fillMaxSize() @@ -49,7 +49,7 @@ fun SignedOutView( content = { SignedOutContent() }, footer = { SignedOutFooter( - onSignInAgain = { state.eventSink(SignedOutEvents.SignInAgain) }, + onSignInAgain = { state.eventSink(SignedOutEvent.SignInAgain) }, ) } ) diff --git a/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/SignedOutPresenterTest.kt b/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/SignedOutPresenterTest.kt index 519016b1e0..b72ebbcdba 100644 --- a/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/SignedOutPresenterTest.kt +++ b/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/SignedOutPresenterTest.kt @@ -60,7 +60,7 @@ class SignedOutPresenterTest { assertThat(initialState.signedOutSession).isEqualTo(aSessionData) assertThat(sessionStore.getAllSessions()).isNotEmpty() assertThat(sessionStore.numberOfSessions()).isEqualTo(1) - initialState.eventSink(SignedOutEvents.SignInAgain) + initialState.eventSink(SignedOutEvent.SignInAgain) assertThat(awaitItem().signedOutSession).isNull() assertThat(sessionStore.getAllSessions()).isEmpty() assertThat(sessionStore.numberOfSessions()).isEqualTo(0) From 15cdd8535719e5ae365bff98aace4483fc0f6bad Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:25:19 +0200 Subject: [PATCH 060/112] `LeaveSpaceEvents` -> `LeaveSpaceEvent` --- ...{LeaveSpaceEvents.kt => LeaveSpaceEvent.kt} | 14 +++++++------- .../space/impl/leave/LeaveSpacePresenter.kt | 14 +++++++------- .../space/impl/leave/LeaveSpaceState.kt | 2 +- .../space/impl/leave/LeaveSpaceView.kt | 12 ++++++------ .../impl/leave/LeaveSpacePresenterTest.kt | 18 +++++++++--------- 5 files changed, 30 insertions(+), 30 deletions(-) rename features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/{LeaveSpaceEvents.kt => LeaveSpaceEvent.kt} (59%) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceEvents.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceEvent.kt similarity index 59% rename from features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceEvents.kt rename to features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceEvent.kt index 8bc96c428c..debec67151 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceEvents.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceEvent.kt @@ -10,11 +10,11 @@ package io.element.android.features.space.impl.leave import io.element.android.libraries.matrix.api.core.RoomId -sealed interface LeaveSpaceEvents { - data object Retry : LeaveSpaceEvents - data object SelectAllRooms : LeaveSpaceEvents - data object DeselectAllRooms : LeaveSpaceEvents - data class ToggleRoomSelection(val roomId: RoomId) : LeaveSpaceEvents - data object LeaveSpace : LeaveSpaceEvents - data object CloseError : LeaveSpaceEvents +sealed interface LeaveSpaceEvent { + data object Retry : LeaveSpaceEvent + data object SelectAllRooms : LeaveSpaceEvent + data object DeselectAllRooms : LeaveSpaceEvent + data class ToggleRoomSelection(val roomId: RoomId) : LeaveSpaceEvent + data object LeaveSpace : LeaveSpaceEvent + data object CloseError : LeaveSpaceEvent } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenter.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenter.kt index e0876c255c..91a4552903 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenter.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenter.kt @@ -99,33 +99,33 @@ class LeaveSpacePresenter( } } - fun handleEvent(event: LeaveSpaceEvents) { + fun handleEvent(event: LeaveSpaceEvent) { when (event) { - LeaveSpaceEvents.Retry -> { + LeaveSpaceEvent.Retry -> { leaveSpaceRooms = AsyncData.Loading() retryCount += 1 } - LeaveSpaceEvents.DeselectAllRooms -> { + LeaveSpaceEvent.DeselectAllRooms -> { selectedRoomIds = persistentSetOf() } - LeaveSpaceEvents.SelectAllRooms -> { + LeaveSpaceEvent.SelectAllRooms -> { selectedRoomIds = selectableSpaceRooms.dataOrNull() .orEmpty() .filter { it.isLastOwner.not() } .map { it.spaceRoom.roomId } } - is LeaveSpaceEvents.ToggleRoomSelection -> { + is LeaveSpaceEvent.ToggleRoomSelection -> { selectedRoomIds = if (selectedRoomIds.contains(event.roomId)) { selectedRoomIds - event.roomId } else { selectedRoomIds + event.roomId } } - LeaveSpaceEvents.LeaveSpace -> coroutineScope.leaveSpace( + LeaveSpaceEvent.LeaveSpace -> coroutineScope.leaveSpace( leaveSpaceAction = leaveSpaceAction, selectedRoomIds = selectedRoomIds, ) - LeaveSpaceEvents.CloseError -> { + LeaveSpaceEvent.CloseError -> { leaveSpaceAction.value = AsyncAction.Uninitialized } } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceState.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceState.kt index 9bf029cb15..5f0423721e 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceState.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceState.kt @@ -19,7 +19,7 @@ data class LeaveSpaceState( val areCreatorsPrivileged: Boolean, val selectableSpaceRooms: AsyncData>, val leaveSpaceAction: AsyncAction, - val eventSink: (LeaveSpaceEvents) -> Unit, + val eventSink: (LeaveSpaceEvent) -> Unit, ) { private val rooms = selectableSpaceRooms.dataOrNull().orEmpty().toImmutableList() private val lastAdminRooms: ImmutableList diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt index 992240cac2..e9e13c2c0e 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt @@ -92,7 +92,7 @@ fun LeaveSpaceView( showLeaveButton = state.showLeaveButton, selectedRoomsCount = state.selectedRoomsCount, onLeaveSpace = { - state.eventSink(LeaveSpaceEvents.LeaveSpace) + state.eventSink(LeaveSpaceEvent.LeaveSpace) }, onCancel = onCancel, showRolesAndPermissionsButton = state.needsOwnerChange && !state.areCreatorsPrivileged, @@ -115,7 +115,7 @@ fun LeaveSpaceView( selectableSpaceRoom = selectableSpaceRoom, showCheckBox = state.hasOnlyLastAdminRoom.not(), onClick = { - state.eventSink(LeaveSpaceEvents.ToggleRoomSelection(selectableSpaceRoom.spaceRoom.roomId)) + state.eventSink(LeaveSpaceEvent.ToggleRoomSelection(selectableSpaceRoom.spaceRoom.roomId)) } ) } @@ -125,7 +125,7 @@ fun LeaveSpaceView( AsyncFailure( throwable = state.selectableSpaceRooms.error, onRetry = { - state.eventSink(LeaveSpaceEvents.Retry) + state.eventSink(LeaveSpaceEvent.Retry) }, ) } @@ -143,7 +143,7 @@ fun LeaveSpaceView( async = state.leaveSpaceAction, onSuccess = { /* Nothing to do, the screen will be dismissed automatically */ }, errorMessage = { stringResource(CommonStrings.error_unknown) }, - onErrorDismiss = { state.eventSink(LeaveSpaceEvents.CloseError) }, + onErrorDismiss = { state.eventSink(LeaveSpaceEvent.CloseError) }, ) } @@ -184,11 +184,11 @@ private fun LeaveSpaceHeader( if (state.showQuickAction) { if (state.areAllSelected) { QuickActionButton(CommonStrings.action_deselect_all) { - state.eventSink(LeaveSpaceEvents.DeselectAllRooms) + state.eventSink(LeaveSpaceEvent.DeselectAllRooms) } } else { QuickActionButton(resId = CommonStrings.action_select_all) { - state.eventSink(LeaveSpaceEvents.SelectAllRooms) + state.eventSink(LeaveSpaceEvent.SelectAllRooms) } } } diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenterTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenterTest.kt index 021f52defd..4f7bb78c80 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenterTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenterTest.kt @@ -61,7 +61,7 @@ class LeaveSpacePresenterTest { val stateError = awaitItem() assertThat(stateError.selectableSpaceRooms.isFailure()).isTrue() // Retry - stateError.eventSink(LeaveSpaceEvents.Retry) + stateError.eventSink(LeaveSpaceEvent.Retry) skipItems(1) val stateLoadingAgain = awaitItem() assertThat(stateLoadingAgain.selectableSpaceRooms.isLoading()).isTrue() @@ -121,7 +121,7 @@ class LeaveSpacePresenterTest { assertThat(finalState.selectableSpaceRooms.dataOrNull()!!.map { it.spaceRoom.roomId }).containsExactly(A_ROOM_ID, A_ROOM_ID_3) assertThat(finalState.selectedRoomsCount).isEqualTo(2) // Leaving the space will not include the DM - finalState.eventSink(LeaveSpaceEvents.LeaveSpace) + finalState.eventSink(LeaveSpaceEvent.LeaveSpace) val stateLeaving = awaitItem() assertThat(stateLeaving.leaveSpaceAction).isEqualTo(AsyncAction.Loading) val stateLeft = awaitItem() @@ -165,32 +165,32 @@ class LeaveSpacePresenterTest { assertThat(room2.isSelected).isFalse() assertThat(room2.isLastOwner).isTrue() // Deselect all - state.eventSink(LeaveSpaceEvents.DeselectAllRooms) + state.eventSink(LeaveSpaceEvent.DeselectAllRooms) skipItems(1) val stateAllDeselected = awaitItem() val dataAllDeselected = stateAllDeselected.selectableSpaceRooms.dataOrNull()!! assertThat(dataAllDeselected.any { it.isSelected }).isFalse() // Select all - stateAllDeselected.eventSink(LeaveSpaceEvents.SelectAllRooms) + stateAllDeselected.eventSink(LeaveSpaceEvent.SelectAllRooms) skipItems(1) val stateAllSelected = awaitItem() val dataAllSelected = stateAllSelected.selectableSpaceRooms.dataOrNull()!! // The last admin room should not be selected assertThat(dataAllSelected.count { it.isSelected }).isEqualTo(1) // Toggle selection of the first room - stateAllSelected.eventSink(LeaveSpaceEvents.ToggleRoomSelection(A_ROOM_ID)) + stateAllSelected.eventSink(LeaveSpaceEvent.ToggleRoomSelection(A_ROOM_ID)) skipItems(1) val stateOneDeselected = awaitItem() val dataOneDeselected = stateOneDeselected.selectableSpaceRooms.dataOrNull()!! assertThat(dataOneDeselected[0].isSelected).isFalse() // Toggle selection of the first room - stateOneDeselected.eventSink(LeaveSpaceEvents.ToggleRoomSelection(A_ROOM_ID)) + stateOneDeselected.eventSink(LeaveSpaceEvent.ToggleRoomSelection(A_ROOM_ID)) skipItems(1) val stateOneSelected = awaitItem() val dataOneSelected = stateOneSelected.selectableSpaceRooms.dataOrNull()!! assertThat(dataOneSelected[0].isSelected).isTrue() // Leave space - stateOneSelected.eventSink(LeaveSpaceEvents.LeaveSpace) + stateOneSelected.eventSink(LeaveSpaceEvent.LeaveSpace) val stateLeaving = awaitItem() assertThat(stateLeaving.leaveSpaceAction).isEqualTo(AsyncAction.Loading) val stateLeft = awaitItem() @@ -215,13 +215,13 @@ class LeaveSpacePresenterTest { presenter.test { skipItems(3) val state = awaitItem() - state.eventSink(LeaveSpaceEvents.LeaveSpace) + state.eventSink(LeaveSpaceEvent.LeaveSpace) val stateLeaving = awaitItem() assertThat(stateLeaving.leaveSpaceAction).isEqualTo(AsyncAction.Loading) val stateError = awaitItem() assertThat(stateError.leaveSpaceAction.isFailure()).isTrue() // Close error - stateError.eventSink(LeaveSpaceEvents.CloseError) + stateError.eventSink(LeaveSpaceEvent.CloseError) val stateErrorClosed = awaitItem() assertThat(stateErrorClosed.leaveSpaceAction).isEqualTo(AsyncAction.Uninitialized) } From d07eba9a202b1149be351e8ac660cedf3f377b63 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:26:02 +0200 Subject: [PATCH 061/112] `SpaceEvents` -> `SpaceEvent` --- .../root/{SpaceEvents.kt => SpaceEvent.kt} | 28 ++++---- .../space/impl/root/SpacePresenter.kt | 28 ++++---- .../features/space/impl/root/SpaceState.kt | 2 +- .../space/impl/root/SpaceStatePreviewParam.kt | 2 +- .../features/space/impl/root/SpaceView.kt | 32 ++++----- .../space/impl/root/SpacePresenterTest.kt | 72 +++++++++---------- .../features/space/impl/root/SpaceViewTest.kt | 36 +++++----- 7 files changed, 100 insertions(+), 100 deletions(-) rename features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/{SpaceEvents.kt => SpaceEvent.kt} (54%) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceEvents.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceEvent.kt similarity index 54% rename from features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceEvents.kt rename to features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceEvent.kt index 0f17a2f6f7..1761ec0846 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceEvents.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceEvent.kt @@ -11,21 +11,21 @@ package io.element.android.features.space.impl.root import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.spaces.SpaceRoom -sealed interface SpaceEvents { - data object LoadMore : SpaceEvents - data class Join(val spaceRoom: SpaceRoom) : SpaceEvents - data object ClearFailures : SpaceEvents - data class AcceptInvite(val spaceRoom: SpaceRoom) : SpaceEvents - data class DeclineInvite(val spaceRoom: SpaceRoom) : SpaceEvents +sealed interface SpaceEvent { + data object LoadMore : SpaceEvent + data class Join(val spaceRoom: SpaceRoom) : SpaceEvent + data object ClearFailures : SpaceEvent + data class AcceptInvite(val spaceRoom: SpaceRoom) : SpaceEvent + data class DeclineInvite(val spaceRoom: SpaceRoom) : SpaceEvent - data class ShowTopicViewer(val topic: String) : SpaceEvents - data object HideTopicViewer : SpaceEvents + data class ShowTopicViewer(val topic: String) : SpaceEvent + data object HideTopicViewer : SpaceEvent // Manage mode events - data object EnterManageMode : SpaceEvents - data object ExitManageMode : SpaceEvents - data class ToggleRoomSelection(val roomId: RoomId) : SpaceEvents - data object ConfirmRoomRemoval : SpaceEvents - data object RemoveSelectedRooms : SpaceEvents - data object ClearRemoveAction : SpaceEvents + data object EnterManageMode : SpaceEvent + data object ExitManageMode : SpaceEvent + data class ToggleRoomSelection(val roomId: RoomId) : SpaceEvent + data object ConfirmRoomRemoval : SpaceEvent + data object RemoveSelectedRooms : SpaceEvent + data object ClearRemoveAction : SpaceEvent } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt index dd5704e0d6..d42501d002 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt @@ -150,51 +150,51 @@ class SpacePresenter( } } - fun handleEvent(event: SpaceEvents) { + fun handleEvent(event: SpaceEvent) { when (event) { // SpaceRoomList is loaded automatically as backend is really slow. Event is kept for future. - SpaceEvents.LoadMore -> Unit - is SpaceEvents.Join -> { + SpaceEvent.LoadMore -> Unit + is SpaceEvent.Join -> { sessionCoroutineScope.joinRoom(event.spaceRoom, joinActions, setJoinActions) } - SpaceEvents.ClearFailures -> { + SpaceEvent.ClearFailures -> { val failedActions = joinActions .filterValues { it is AsyncAction.Failure } .mapValues { AsyncAction.Uninitialized } setJoinActions(joinActions + failedActions) } - is SpaceEvents.AcceptInvite -> { + is SpaceEvent.AcceptInvite -> { acceptDeclineInviteState.eventSink( AcceptDeclineInviteEvent.AcceptInvite(event.spaceRoom.toInviteData()) ) } - is SpaceEvents.DeclineInvite -> { + is SpaceEvent.DeclineInvite -> { acceptDeclineInviteState.eventSink( AcceptDeclineInviteEvent.DeclineInvite(invite = event.spaceRoom.toInviteData(), shouldConfirm = true, blockUser = false) ) } - SpaceEvents.HideTopicViewer -> topicViewerState = TopicViewerState.Hidden - is SpaceEvents.ShowTopicViewer -> topicViewerState = TopicViewerState.Shown(event.topic) + SpaceEvent.HideTopicViewer -> topicViewerState = TopicViewerState.Hidden + is SpaceEvent.ShowTopicViewer -> topicViewerState = TopicViewerState.Shown(event.topic) // Manage mode events - SpaceEvents.EnterManageMode -> { + SpaceEvent.EnterManageMode -> { isManageMode = true selectedRoomIds = emptySet() } - SpaceEvents.ExitManageMode -> { + SpaceEvent.ExitManageMode -> { localCoroutineScope.launch { exitManageMode(shouldReset = removedRoomIds.isNotEmpty()) } } - is SpaceEvents.ToggleRoomSelection -> { + is SpaceEvent.ToggleRoomSelection -> { selectedRoomIds = if (event.roomId in selectedRoomIds) { selectedRoomIds - event.roomId } else { selectedRoomIds + event.roomId } } - SpaceEvents.RemoveSelectedRooms -> { + SpaceEvent.RemoveSelectedRooms -> { removeRoomsAction = AsyncAction.ConfirmingNoParams } - SpaceEvents.ConfirmRoomRemoval -> { + SpaceEvent.ConfirmRoomRemoval -> { localCoroutineScope.launch { removeRoomsAction = AsyncAction.Loading val spaceId = spaceRoomList.spaceId @@ -219,7 +219,7 @@ class SpacePresenter( } } } - SpaceEvents.ClearRemoveAction -> { + SpaceEvent.ClearRemoveAction -> { removeRoomsAction = AsyncAction.Uninitialized } } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceState.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceState.kt index a669c294b5..a782b9fa71 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceState.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceState.kt @@ -32,7 +32,7 @@ data class SpaceState( val selectedRoomIds: ImmutableSet, val canEditSpaceGraph: Boolean, val removeRoomsAction: AsyncAction, - val eventSink: (SpaceEvents) -> Unit + val eventSink: (SpaceEvent) -> Unit ) { fun isJoining(spaceId: RoomId): Boolean = joinActions[spaceId] == AsyncAction.Loading fun isSelected(spaceId: RoomId): Boolean = selectedRoomIds.contains(spaceId) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStatePreviewParam.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStatePreviewParam.kt index 34d6e4534a..ee44c8c17e 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStatePreviewParam.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStatePreviewParam.kt @@ -80,7 +80,7 @@ fun aSpaceState( selectedRoomIds: Set = emptySet(), canManageRooms: Boolean = true, removeRoomsAction: AsyncAction = AsyncAction.Uninitialized, - eventSink: (SpaceEvents) -> Unit = { }, + eventSink: (SpaceEvent) -> Unit = { }, ) = SpaceState( spaceInfo = spaceInfo, children = children.toImmutableList(), diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt index f1cddf5e0f..069b00492c 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt @@ -114,7 +114,7 @@ fun SpaceView( var handledBack by remember { mutableStateOf(false) } BackHandler(enabled = !handledBack) { if (state.isManageMode) { - state.eventSink(SpaceEvents.ExitManageMode) + state.eventSink(SpaceEvent.ExitManageMode) } else { handledBack = true onBackClick() @@ -133,8 +133,8 @@ fun SpaceView( ManageModeTopBar( selectedCount = state.selectedCount, isRemoveButtonEnabled = state.isRemoveButtonEnabled, - onCancelClick = { state.eventSink(SpaceEvents.ExitManageMode) }, - onRemoveClick = { state.eventSink(SpaceEvents.RemoveSelectedRooms) }, + onCancelClick = { state.eventSink(SpaceEvent.ExitManageMode) }, + onRemoveClick = { state.eventSink(SpaceEvent.RemoveSelectedRooms) }, ) } AnimatedVisibility( @@ -152,7 +152,7 @@ fun SpaceView( onSettingsClick = onSettingsClick, onShareSpace = onShareSpace, onViewMembersClick = onViewMembersClick, - onManageRoomsClick = { state.eventSink(SpaceEvents.EnterManageMode) }, + onManageRoomsClick = { state.eventSink(SpaceEvent.EnterManageMode) }, onAddRoomClick = onAddRoomClick, onCreateRoomClick = onCreateRoomClick, ) @@ -168,13 +168,13 @@ fun SpaceView( state = state, onRoomClick = { spaceRoom -> if (state.isManageMode) { - state.eventSink(SpaceEvents.ToggleRoomSelection(spaceRoom.roomId)) + state.eventSink(SpaceEvent.ToggleRoomSelection(spaceRoom.roomId)) } else { onRoomClick(spaceRoom) } }, onTopicClick = { topic -> - state.eventSink(SpaceEvents.ShowTopicViewer(topic)) + state.eventSink(SpaceEvent.ShowTopicViewer(topic)) }, onCreateRoomClick = onCreateRoomClick, onAddRoomClick = onAddRoomClick, @@ -187,8 +187,8 @@ fun SpaceView( spaceDisplayName = state.spaceInfo.name ?: state.spaceInfo.id.value, removeRoomsAction = state.removeRoomsAction, selectedCount = state.selectedCount, - onConfirm = { state.eventSink(SpaceEvents.ConfirmRoomRemoval) }, - onDismiss = { state.eventSink(SpaceEvents.ClearRemoveAction) }, + onConfirm = { state.eventSink(SpaceEvent.ConfirmRoomRemoval) }, + onDismiss = { state.eventSink(SpaceEvent.ClearRemoveAction) }, ) acceptDeclineInviteView() } @@ -198,7 +198,7 @@ fun SpaceView( TopicViewerBottomSheet( topicViewerState = state.topicViewerState, onDismiss = { - state.eventSink(SpaceEvents.HideTopicViewer) + state.eventSink(SpaceEvent.HideTopicViewer) } ) } @@ -207,7 +207,7 @@ fun SpaceView( @Composable private fun JoinFailuresEffect( hasAnyFailure: Boolean, - eventSink: (SpaceEvents) -> Unit, + eventSink: (SpaceEvent) -> Unit, ) { val asyncIndicatorState = rememberAsyncIndicatorState() val updatedEventSink by rememberUpdatedState(eventSink) @@ -218,7 +218,7 @@ private fun JoinFailuresEffect( AsyncIndicator.Failure(text = stringResource(CommonStrings.common_something_went_wrong)) } delay(AsyncIndicator.DURATION_SHORT) - updatedEventSink(SpaceEvents.ClearFailures) + updatedEventSink(SpaceEvent.ClearFailures) } else { asyncIndicatorState.clear() } @@ -317,7 +317,7 @@ private fun SpaceViewContent( } } else { spaceRoom.trailingAction(isCurrentlyJoining = isCurrentlyJoining) { - state.eventSink(SpaceEvents.Join(spaceRoom)) + state.eventSink(SpaceEvent.Join(spaceRoom)) } }, bottomAction = if (state.isManageMode) { @@ -325,10 +325,10 @@ private fun SpaceViewContent( } else { spaceRoom.inviteButtons( onAcceptClick = { - state.eventSink(SpaceEvents.AcceptInvite(spaceRoom)) + state.eventSink(SpaceEvent.AcceptInvite(spaceRoom)) }, onDeclineClick = { - state.eventSink(SpaceEvents.DeclineInvite(spaceRoom)) + state.eventSink(SpaceEvent.DeclineInvite(spaceRoom)) } ) } @@ -384,7 +384,7 @@ private fun EmptySpaceView( @Composable private fun LoadingMoreIndicator( - eventSink: (SpaceEvents) -> Unit, + eventSink: (SpaceEvent) -> Unit, modifier: Modifier = Modifier ) { Box( @@ -397,7 +397,7 @@ private fun LoadingMoreIndicator( ) val latestEventSink by rememberUpdatedState(eventSink) LaunchedEffect(Unit) { - latestEventSink(SpaceEvents.LoadMore) + latestEventSink(SpaceEvent.LoadMore) } } } diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt index 6ece20f455..627ea4483d 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt @@ -106,7 +106,7 @@ class SpacePresenterTest { presenter.test { val state = awaitItem() // LoadMore event should not cause any state change - state.eventSink(SpaceEvents.LoadMore) + state.eventSink(SpaceEvent.LoadMore) expectNoEvents() } } @@ -181,7 +181,7 @@ class SpacePresenterTest { skipItems(1) val state = awaitItem() assertThat(state.joinActions[A_ROOM_ID_2]).isNull() - state.eventSink(SpaceEvents.Join(aNotJoinedRoom)) + state.eventSink(SpaceEvent.Join(aNotJoinedRoom)) val joiningState = awaitItem() assertThat(joiningState.joinActions[A_ROOM_ID_2]).isEqualTo(AsyncAction.Loading) // Let the joinRoom call complete @@ -234,14 +234,14 @@ class SpacePresenterTest { skipItems(1) val state = awaitItem() assertThat(state.joinActions[A_ROOM_ID_2]).isNull() - state.eventSink(SpaceEvents.Join(aNotJoinedRoom)) + state.eventSink(SpaceEvent.Join(aNotJoinedRoom)) val joiningState = awaitItem() assertThat(joiningState.joinActions[A_ROOM_ID_2]).isEqualTo(AsyncAction.Loading) val errorState = awaitItem() // Joined room is removed from the join actions assertThat(errorState.joinActions[A_ROOM_ID_2]!!.isFailure()).isTrue() // Clear error - errorState.eventSink(SpaceEvents.ClearFailures) + errorState.eventSink(SpaceEvent.ClearFailures) val clearedState = awaitItem() assertThat(clearedState.joinActions[A_ROOM_ID_2]).isEqualTo(AsyncAction.Uninitialized) } @@ -258,9 +258,9 @@ class SpacePresenterTest { val state = awaitItem() assertThat(state.topicViewerState).isEqualTo(TopicViewerState.Hidden) advanceUntilIdle() - state.eventSink(SpaceEvents.ShowTopicViewer("topic")) + state.eventSink(SpaceEvent.ShowTopicViewer("topic")) assertThat(awaitItem().topicViewerState).isEqualTo(TopicViewerState.Shown("topic")) - state.eventSink(SpaceEvents.HideTopicViewer) + state.eventSink(SpaceEvent.HideTopicViewer) assertThat(awaitItem().topicViewerState).isEqualTo(TopicViewerState.Hidden) } } @@ -300,14 +300,14 @@ class SpacePresenterTest { val state = awaitItem() assertThat(state.joinActions[A_ROOM_ID_2]).isNull() if (acceptInvite) { - state.eventSink(SpaceEvents.AcceptInvite(anInvitedRoom)) + state.eventSink(SpaceEvent.AcceptInvite(anInvitedRoom)) eventRecorder.assertSingle( AcceptDeclineInviteEvent.AcceptInvite( invite = anInvitedRoom.toInviteData(), ) ) } else { - state.eventSink(SpaceEvents.DeclineInvite(anInvitedRoom)) + state.eventSink(SpaceEvent.DeclineInvite(anInvitedRoom)) eventRecorder.assertSingle( AcceptDeclineInviteEvent.DeclineInvite( invite = anInvitedRoom.toInviteData(), @@ -325,7 +325,7 @@ class SpacePresenterTest { presenter.test { val state = awaitItem() assertThat(state.isManageMode).isFalse() - state.eventSink(SpaceEvents.EnterManageMode) + state.eventSink(SpaceEvent.EnterManageMode) val manageModeState = awaitItem() assertThat(manageModeState.isManageMode).isTrue() assertThat(manageModeState.selectedRoomIds).isEmpty() @@ -342,9 +342,9 @@ class SpacePresenterTest { val presenter = createSpacePresenter(spaceRoomList = fakeSpaceRoomList) presenter.test { val initialState = awaitItem() - initialState.eventSink(SpaceEvents.EnterManageMode) - initialState.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID)) - initialState.eventSink(SpaceEvents.ExitManageMode) + initialState.eventSink(SpaceEvent.EnterManageMode) + initialState.eventSink(SpaceEvent.ToggleRoomSelection(A_ROOM_ID)) + initialState.eventSink(SpaceEvent.ExitManageMode) advanceUntilIdle() val finalState = expectMostRecentItem() assertThat(finalState.isManageMode).isFalse() @@ -359,13 +359,13 @@ class SpacePresenterTest { val presenter = createSpacePresenter() presenter.test { val initialState = awaitItem() - initialState.eventSink(SpaceEvents.EnterManageMode) + initialState.eventSink(SpaceEvent.EnterManageMode) // Select a room - initialState.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID)) + initialState.eventSink(SpaceEvent.ToggleRoomSelection(A_ROOM_ID)) var latestState = expectMostRecentItem() assertThat(latestState.selectedRoomIds).containsExactly(A_ROOM_ID) // Deselect the room - latestState.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID)) + latestState.eventSink(SpaceEvent.ToggleRoomSelection(A_ROOM_ID)) latestState = expectMostRecentItem() assertThat(latestState.selectedRoomIds).isEmpty() } @@ -397,10 +397,10 @@ class SpacePresenterTest { advanceUntilIdle() val stateWithChildren = awaitItem() assertThat(stateWithChildren.children).hasSize(1) - stateWithChildren.eventSink(SpaceEvents.EnterManageMode) - stateWithChildren.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID)) - stateWithChildren.eventSink(SpaceEvents.RemoveSelectedRooms) - stateWithChildren.eventSink(SpaceEvents.ConfirmRoomRemoval) + stateWithChildren.eventSink(SpaceEvent.EnterManageMode) + stateWithChildren.eventSink(SpaceEvent.ToggleRoomSelection(A_ROOM_ID)) + stateWithChildren.eventSink(SpaceEvent.RemoveSelectedRooms) + stateWithChildren.eventSink(SpaceEvent.ConfirmRoomRemoval) advanceUntilIdle() val successState = expectMostRecentItem() assertThat(successState.removeRoomsAction).isEqualTo(AsyncAction.Success(Unit)) @@ -442,11 +442,11 @@ class SpacePresenterTest { advanceUntilIdle() val stateWithChildren = awaitItem() assertThat(stateWithChildren.children).hasSize(2) - stateWithChildren.eventSink(SpaceEvents.EnterManageMode) - stateWithChildren.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID)) - stateWithChildren.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID_2)) - stateWithChildren.eventSink(SpaceEvents.RemoveSelectedRooms) - stateWithChildren.eventSink(SpaceEvents.ConfirmRoomRemoval) + stateWithChildren.eventSink(SpaceEvent.EnterManageMode) + stateWithChildren.eventSink(SpaceEvent.ToggleRoomSelection(A_ROOM_ID)) + stateWithChildren.eventSink(SpaceEvent.ToggleRoomSelection(A_ROOM_ID_2)) + stateWithChildren.eventSink(SpaceEvent.RemoveSelectedRooms) + stateWithChildren.eventSink(SpaceEvent.ConfirmRoomRemoval) advanceUntilIdle() val failureState = expectMostRecentItem() assertThat(failureState.removeRoomsAction.isFailure()).isTrue() @@ -492,16 +492,16 @@ class SpacePresenterTest { awaitItem() // Initial empty state advanceUntilIdle() val stateWithChildren = awaitItem() - stateWithChildren.eventSink(SpaceEvents.EnterManageMode) - stateWithChildren.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID)) - stateWithChildren.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID_2)) - stateWithChildren.eventSink(SpaceEvents.RemoveSelectedRooms) - stateWithChildren.eventSink(SpaceEvents.ConfirmRoomRemoval) + stateWithChildren.eventSink(SpaceEvent.EnterManageMode) + stateWithChildren.eventSink(SpaceEvent.ToggleRoomSelection(A_ROOM_ID)) + stateWithChildren.eventSink(SpaceEvent.ToggleRoomSelection(A_ROOM_ID_2)) + stateWithChildren.eventSink(SpaceEvent.RemoveSelectedRooms) + stateWithChildren.eventSink(SpaceEvent.ConfirmRoomRemoval) advanceUntilIdle() val failureState = expectMostRecentItem() assertThat(failureState.removeRoomsAction.isFailure()).isTrue() // Exit manage mode after partial failure - reset should be called - failureState.eventSink(SpaceEvents.ExitManageMode) + failureState.eventSink(SpaceEvent.ExitManageMode) advanceUntilIdle() expectMostRecentItem() assert(resetResult).isCalledOnce() @@ -530,7 +530,7 @@ class SpacePresenterTest { // Both room and space visible initially assertThat(stateWithChildren.children).hasSize(2) assertThat(stateWithChildren.isManageMode).isFalse() - stateWithChildren.eventSink(SpaceEvents.EnterManageMode) + stateWithChildren.eventSink(SpaceEvent.EnterManageMode) val manageModeState = expectMostRecentItem() // Only rooms visible in manage mode assertThat(manageModeState.children).hasSize(1) @@ -648,12 +648,12 @@ class SpacePresenterTest { awaitItem() // Initial empty state advanceUntilIdle() val stateWithChildren = awaitItem() - stateWithChildren.eventSink(SpaceEvents.EnterManageMode) + stateWithChildren.eventSink(SpaceEvent.EnterManageMode) // Select both rooms for removal - stateWithChildren.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID)) - stateWithChildren.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID_2)) - stateWithChildren.eventSink(SpaceEvents.RemoveSelectedRooms) - stateWithChildren.eventSink(SpaceEvents.ConfirmRoomRemoval) + stateWithChildren.eventSink(SpaceEvent.ToggleRoomSelection(A_ROOM_ID)) + stateWithChildren.eventSink(SpaceEvent.ToggleRoomSelection(A_ROOM_ID_2)) + stateWithChildren.eventSink(SpaceEvent.RemoveSelectedRooms) + stateWithChildren.eventSink(SpaceEvent.ConfirmRoomRemoval) advanceUntilIdle() val failureState = expectMostRecentItem() assertThat(failureState.removeRoomsAction.isFailure()).isTrue() diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt index 042d6a00ff..218e635e0c 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt @@ -42,7 +42,7 @@ import org.robolectric.annotation.Config class SpaceViewTest : RobolectricTest() { @Test fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setSpaceView( aSpaceState( @@ -58,7 +58,7 @@ class SpaceViewTest : RobolectricTest() { @Test fun `clicking on a room name invokes the expected callback`() = runAndroidComposeUiTest { val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, displayName = A_ROOM_NAME) - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithParam(aSpaceRoom) { setSpaceView( aSpaceState( @@ -75,7 +75,7 @@ class SpaceViewTest : RobolectricTest() { @Test fun `clicking on Join room emits the expected Event`() = runAndroidComposeUiTest { val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, state = null) - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setSpaceView( aSpaceState( children = listOf(aSpaceRoom), @@ -84,14 +84,14 @@ class SpaceViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_join) - eventsRecorder.assertSingle(SpaceEvents.Join(aSpaceRoom)) + eventsRecorder.assertSingle(SpaceEvent.Join(aSpaceRoom)) } @Config(qualifiers = "h1024dp") @Test fun `clicking on accept invite emits the expected Event`() = runAndroidComposeUiTest { val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, state = CurrentUserMembership.INVITED) - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setSpaceView( aSpaceState( hasMoreToLoad = false, @@ -100,14 +100,14 @@ class SpaceViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_accept) - eventsRecorder.assertSingle(SpaceEvents.AcceptInvite(aSpaceRoom)) + eventsRecorder.assertSingle(SpaceEvent.AcceptInvite(aSpaceRoom)) } @Config(qualifiers = "h1024dp") @Test fun `clicking on decline invite emits the expected Event`() = runAndroidComposeUiTest { val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, state = CurrentUserMembership.INVITED) - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setSpaceView( aSpaceState( hasMoreToLoad = false, @@ -116,13 +116,13 @@ class SpaceViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_decline) - eventsRecorder.assertSingle(SpaceEvents.DeclineInvite(aSpaceRoom)) + eventsRecorder.assertSingle(SpaceEvent.DeclineInvite(aSpaceRoom)) } @Config(qualifiers = "h1024dp") @Test fun `clicking on topic emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setSpaceView( aSpaceState( spaceInfo = aRoomInfo(topic = A_ROOM_TOPIC), @@ -131,12 +131,12 @@ class SpaceViewTest : RobolectricTest() { ) ) onNodeWithText(A_ROOM_TOPIC).performClick() - eventsRecorder.assertSingle(SpaceEvents.ShowTopicViewer(A_ROOM_TOPIC)) + eventsRecorder.assertSingle(SpaceEvent.ShowTopicViewer(A_ROOM_TOPIC)) } @Test fun `clicking back in manage mode emits ExitManageMode event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setSpaceView( aSpaceState( hasMoreToLoad = false, @@ -145,13 +145,13 @@ class SpaceViewTest : RobolectricTest() { ) ) pressBackKey() - eventsRecorder.assertSingle(SpaceEvents.ExitManageMode) + eventsRecorder.assertSingle(SpaceEvent.ExitManageMode) } @Test fun `clicking on room in manage mode emits ToggleRoomSelection event`() = runAndroidComposeUiTest { val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, displayName = A_ROOM_NAME) - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setSpaceView( aSpaceState( children = listOf(aSpaceRoom), @@ -161,12 +161,12 @@ class SpaceViewTest : RobolectricTest() { ) ) onNodeWithText(A_ROOM_NAME).performClick() - eventsRecorder.assertSingle(SpaceEvents.ToggleRoomSelection(A_ROOM_ID)) + eventsRecorder.assertSingle(SpaceEvent.ToggleRoomSelection(A_ROOM_ID)) } @Test fun `clicking remove button emits RemoveSelectedRooms event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setSpaceView( aSpaceState( children = listOf(aSpaceRoom(roomId = A_ROOM_ID)), @@ -177,13 +177,13 @@ class SpaceViewTest : RobolectricTest() { ) ) clickOn(CommonStrings.action_remove) - eventsRecorder.assertSingle(SpaceEvents.RemoveSelectedRooms) + eventsRecorder.assertSingle(SpaceEvent.RemoveSelectedRooms) } @Config(qualifiers = "h1024dp") @Test fun `clicking confirm in removal dialog emits ConfirmRoomRemoval event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setSpaceView( aSpaceState( children = listOf(aSpaceRoom(roomId = A_ROOM_ID)), @@ -196,7 +196,7 @@ class SpaceViewTest : RobolectricTest() { ) // Click on the Remove button in the confirmation dialog clickOn(CommonStrings.action_remove, inDialog = true) - eventsRecorder.assertSingle(SpaceEvents.ConfirmRoomRemoval) + eventsRecorder.assertSingle(SpaceEvent.ConfirmRoomRemoval) } @Test From 771840884a97b8ba5253718034ba74fb7ff66099 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:26:43 +0200 Subject: [PATCH 062/112] `SpaceSettingsEvents` -> `SpaceSettingsEvent` --- .../settings/{SpaceSettingsEvents.kt => SpaceSettingsEvent.kt} | 2 +- .../android/features/space/impl/settings/SpaceSettingsState.kt | 2 +- .../space/impl/settings/SpaceSettingsStatePreviewParam.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/{SpaceSettingsEvents.kt => SpaceSettingsEvent.kt} (88%) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsEvents.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsEvent.kt similarity index 88% rename from features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsEvents.kt rename to features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsEvent.kt index 3c2b1e6609..d85191ed73 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsEvents.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsEvent.kt @@ -8,4 +8,4 @@ package io.element.android.features.space.impl.settings -sealed interface SpaceSettingsEvents +sealed interface SpaceSettingsEvent diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsState.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsState.kt index 5c7e9b5c6e..01f65e4fdf 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsState.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsState.kt @@ -20,5 +20,5 @@ data class SpaceSettingsState( val canEditDetails: Boolean, val showRolesAndPermissions: Boolean, val showSecurityAndPrivacy: Boolean, - val eventSink: (SpaceSettingsEvents) -> Unit + val eventSink: (SpaceSettingsEvent) -> Unit ) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStatePreviewParam.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStatePreviewParam.kt index a68a3ce850..d61b1b656c 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStatePreviewParam.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStatePreviewParam.kt @@ -32,7 +32,7 @@ fun aSpaceSettingsState( showRolesAndPermissions: Boolean = false, showSecurityAndPrivacy: Boolean = false, canEditDetails: Boolean = false, - eventSink: (SpaceSettingsEvents) -> Unit = {}, + eventSink: (SpaceSettingsEvent) -> Unit = {}, ) = SpaceSettingsState( roomId = roomId, name = name, From cf7d5ab4b46286821dde804b156fbbb68df1fa24 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:27:26 +0200 Subject: [PATCH 063/112] `StartChatEvents` -> `StartChatEvent` --- .../root/{StartChatEvents.kt => StartChatEvent.kt} | 6 +++--- .../startchat/impl/root/StartChatPresenter.kt | 6 +++--- .../features/startchat/impl/root/StartChatState.kt | 2 +- .../impl/root/StartChatStatePreviewParam.kt | 2 +- .../features/startchat/impl/root/StartChatView.kt | 12 ++++++------ .../startchat/impl/root/StartChatPresenterTest.kt | 14 +++++++------- .../startchat/impl/root/StartChatViewTest.kt | 12 ++++++------ 7 files changed, 27 insertions(+), 27 deletions(-) rename features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/{StartChatEvents.kt => StartChatEvent.kt} (82%) diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatEvents.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatEvent.kt similarity index 82% rename from features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatEvents.kt rename to features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatEvent.kt index 66e135b45c..68f50c41ee 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatEvents.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatEvent.kt @@ -10,7 +10,7 @@ package io.element.android.features.startchat.impl.root import io.element.android.libraries.matrix.api.user.MatrixUser -sealed interface StartChatEvents { - data class StartDM(val matrixUser: MatrixUser) : StartChatEvents - data object CancelStartDM : StartChatEvents +sealed interface StartChatEvent { + data class StartDM(val matrixUser: MatrixUser) : StartChatEvent + data object CancelStartDM : StartChatEvent } diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenter.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenter.kt index 079ece4180..8b1f92222e 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenter.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenter.kt @@ -49,16 +49,16 @@ class StartChatPresenter( val localCoroutineScope = rememberCoroutineScope() val startDmActionState: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } - fun handleEvent(event: StartChatEvents) { + fun handleEvent(event: StartChatEvent) { when (event) { - is StartChatEvents.StartDM -> localCoroutineScope.launch { + is StartChatEvent.StartDM -> localCoroutineScope.launch { startDMAction.execute( matrixUser = event.matrixUser, createIfDmDoesNotExist = startDmActionState.value is AsyncAction.Confirming, actionState = startDmActionState, ) } - StartChatEvents.CancelStartDM -> startDmActionState.value = AsyncAction.Uninitialized + StartChatEvent.CancelStartDM -> startDmActionState.value = AsyncAction.Uninitialized } } diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatState.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatState.kt index e6746e1302..167e4f444a 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatState.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatState.kt @@ -16,5 +16,5 @@ data class StartChatState( val applicationName: String, val userListState: UserListState, val startDmAction: AsyncAction, - val eventSink: (StartChatEvents) -> Unit, + val eventSink: (StartChatEvent) -> Unit, ) diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatStatePreviewParam.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatStatePreviewParam.kt index e8ac1d55a7..bd7c24da41 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatStatePreviewParam.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatStatePreviewParam.kt @@ -72,7 +72,7 @@ fun aCreateRoomRootState( applicationName: String = "Element X Preview", userListState: UserListState = aUserListState(), startDmAction: AsyncAction = AsyncAction.Uninitialized, - eventSink: (StartChatEvents) -> Unit = {}, + eventSink: (StartChatEvent) -> Unit = {}, ) = StartChatState( applicationName = applicationName, userListState = userListState, diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatView.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatView.kt index 350cf15757..3ef807b777 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatView.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatView.kt @@ -94,7 +94,7 @@ fun StartChatView( onSelectUser = { coroutineScope.launch { view.hideKeyboardAndAwaitAnimation() - state.eventSink(StartChatEvents.StartDM(it)) + state.eventSink(StartChatEvent.StartDM(it)) } }, onDeselectUser = { }, @@ -124,21 +124,21 @@ fun StartChatView( errorMessage = { stringResource(R.string.screen_start_chat_error_starting_chat) }, onRetry = { state.userListState.selectedUsers.firstOrNull() - ?.let { state.eventSink(StartChatEvents.StartDM(it)) } + ?.let { state.eventSink(StartChatEvent.StartDM(it)) } // Cancel start DM if there is no more selected user (should not happen) - ?: state.eventSink(StartChatEvents.CancelStartDM) + ?: state.eventSink(StartChatEvent.CancelStartDM) }, - onErrorDismiss = { state.eventSink(StartChatEvents.CancelStartDM) }, + onErrorDismiss = { state.eventSink(StartChatEvent.CancelStartDM) }, confirmationDialog = { data -> if (data is ConfirmingStartDmWithMatrixUser) { CreateDmConfirmationBottomSheet( matrixUser = data.matrixUser, isUserIdentityUnknown = data.isUserIdentityUnknown, onSendInvite = { - state.eventSink(StartChatEvents.StartDM(data.matrixUser)) + state.eventSink(StartChatEvent.StartDM(data.matrixUser)) }, onDismiss = { - state.eventSink(StartChatEvents.CancelStartDM) + state.eventSink(StartChatEvent.CancelStartDM) }, ) } diff --git a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenterTest.kt b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenterTest.kt index 57f6cd2333..39d6f34b21 100644 --- a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenterTest.kt +++ b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenterTest.kt @@ -53,7 +53,7 @@ class StartChatPresenterTest { assertThat(initialState.userListState.isSearchActive).isFalse() assertThat(initialState.userListState.isMultiSelectionEnabled).isFalse() val matrixUser = MatrixUser(UserId("@name:domain")) - initialState.eventSink(StartChatEvents.StartDM(matrixUser)) + initialState.eventSink(StartChatEvent.StartDM(matrixUser)) awaitItem().also { state -> assertThat(state.startDmAction).isEqualTo(startDMFailureResult) executeResult.assertions().isCalledOnce().with( @@ -61,7 +61,7 @@ class StartChatPresenterTest { value(false), any(), ) - state.eventSink(StartChatEvents.CancelStartDM) + state.eventSink(StartChatEvent.CancelStartDM) } awaitItem().also { state -> assertThat(state.startDmAction.isUninitialized()).isTrue() @@ -85,7 +85,7 @@ class StartChatPresenterTest { assertThat(initialState.userListState.isSearchActive).isFalse() assertThat(initialState.userListState.isMultiSelectionEnabled).isFalse() val matrixUser = MatrixUser(UserId("@name:domain")) - initialState.eventSink(StartChatEvents.StartDM(matrixUser)) + initialState.eventSink(StartChatEvent.StartDM(matrixUser)) awaitItem().also { state -> assertThat(state.startDmAction).isEqualTo(startDMSuccessResult) executeResult.assertions().isCalledOnce().with( @@ -109,7 +109,7 @@ class StartChatPresenterTest { presenter.test { val initialState = awaitItem() assertThat(initialState.startDmAction).isInstanceOf(AsyncAction.Uninitialized::class.java) - initialState.eventSink(StartChatEvents.StartDM(matrixUser)) + initialState.eventSink(StartChatEvent.StartDM(matrixUser)) val confirmingState = awaitItem() assertThat(confirmingState.startDmAction).isEqualTo(startDMConfirmationResult) executeResult.assertions().isCalledOnce().with( @@ -118,7 +118,7 @@ class StartChatPresenterTest { any(), ) // Cancelling should not create the DM - confirmingState.eventSink(StartChatEvents.CancelStartDM) + confirmingState.eventSink(StartChatEvent.CancelStartDM) val finalState = awaitItem() assertThat(finalState.startDmAction.isUninitialized()).isTrue() executeResult.assertions().isCalledExactly(1) @@ -137,7 +137,7 @@ class StartChatPresenterTest { presenter.test { val initialState = awaitItem() assertThat(initialState.startDmAction).isInstanceOf(AsyncAction.Uninitialized::class.java) - initialState.eventSink(StartChatEvents.StartDM(matrixUser)) + initialState.eventSink(StartChatEvent.StartDM(matrixUser)) val confirmingState = awaitItem() assertThat(confirmingState.startDmAction).isEqualTo(startDMConfirmationResult) executeResult.assertions().isCalledOnce().with( @@ -146,7 +146,7 @@ class StartChatPresenterTest { any(), ) // Start DM again should invoke the action with createIfDmDoesNotExist = true - confirmingState.eventSink(StartChatEvents.StartDM(matrixUser)) + confirmingState.eventSink(StartChatEvent.StartDM(matrixUser)) executeResult.assertions().isCalledExactly(2).withSequence( listOf(value(matrixUser), value(false), any()), listOf(value(matrixUser), value(true), any()), diff --git a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatViewTest.kt b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatViewTest.kt index 8b5e1da8e6..7b7c046bb2 100644 --- a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatViewTest.kt +++ b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatViewTest.kt @@ -36,7 +36,7 @@ import org.robolectric.annotation.Config class StartChatViewTest : RobolectricTest() { @Test fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setStartChatView( aCreateRoomRootState( @@ -50,7 +50,7 @@ class StartChatViewTest : RobolectricTest() { @Test fun `clicking on New room invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setStartChatView( aCreateRoomRootState( @@ -65,7 +65,7 @@ class StartChatViewTest : RobolectricTest() { @Config(qualifiers = "h1024dp") @Test fun `clicking on Invite people invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setStartChatView( aCreateRoomRootState( @@ -84,7 +84,7 @@ class StartChatViewTest : RobolectricTest() { fun `clicking on a user suggestion invokes the expected callback`() = runAndroidComposeUiTest { val recentDirectRoomList = aRecentDirectRoomList() val firstRoom = recentDirectRoomList[0] - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithParam(firstRoom.roomId) { setStartChatView( aCreateRoomRootState( @@ -102,7 +102,7 @@ class StartChatViewTest : RobolectricTest() { @Config(qualifiers = "h1024dp") @Test fun `clicking on Join room by address invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setStartChatView( aCreateRoomRootState( @@ -116,7 +116,7 @@ class StartChatViewTest : RobolectricTest() { @Test fun `clicking on room directory invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setStartChatView( aCreateRoomRootState( From 51a4704854edaeee08cce61cf0d796773076932f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:28:07 +0200 Subject: [PATCH 064/112] `UserListEvents` -> `UserListEvent` --- .../startchat/impl/components/UserListView.kt | 14 +++++++------- .../impl/userlist/DefaultUserListPresenter.kt | 8 ++++---- .../{UserListEvents.kt => UserListEvent.kt} | 8 ++++---- .../startchat/impl/userlist/UserListState.kt | 2 +- .../impl/userlist/UserListStatePreviewParam.kt | 2 +- .../userlist/DefaultUserListPresenterTest.kt | 18 +++++++++--------- 6 files changed, 26 insertions(+), 26 deletions(-) rename features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/{UserListEvents.kt => UserListEvent.kt} (88%) diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/components/UserListView.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/components/UserListView.kt index 4b62820ef4..e067e13995 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/components/UserListView.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/components/UserListView.kt @@ -24,7 +24,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import io.element.android.features.startchat.impl.userlist.UserListEvents +import io.element.android.features.startchat.impl.userlist.UserListEvent import io.element.android.features.startchat.impl.userlist.UserListState import io.element.android.features.startchat.impl.userlist.UserListStatePreviewParam import io.element.android.libraries.designsystem.components.avatar.AvatarSize @@ -63,13 +63,13 @@ fun UserListView( showLoader = state.showSearchLoader, isMultiSelectionEnable = state.isMultiSelectionEnabled, showBackButton = showBackButton, - onActiveChange = { state.eventSink(UserListEvents.OnSearchActiveChanged(it)) }, + onActiveChange = { state.eventSink(UserListEvent.OnSearchActiveChanged(it)) }, onUserSelect = { - state.eventSink(UserListEvents.AddToSelection(it)) + state.eventSink(UserListEvent.AddToSelection(it)) onSelectUser(it) }, onUserDeselect = { - state.eventSink(UserListEvents.RemoveFromSelection(it)) + state.eventSink(UserListEvent.RemoveFromSelection(it)) onDeselectUser(it) }, ) @@ -80,7 +80,7 @@ fun UserListView( selectedUsers = state.selectedUsers, autoScroll = true, onUserRemove = { - state.eventSink(UserListEvents.RemoveFromSelection(it)) + state.eventSink(UserListEvent.RemoveFromSelection(it)) onDeselectUser(it) }, ) @@ -104,10 +104,10 @@ fun UserListView( checked = isSelected, onCheckedChange = { if (isSelected) { - state.eventSink(UserListEvents.RemoveFromSelection(recentDirectRoom.matrixUser)) + state.eventSink(UserListEvent.RemoveFromSelection(recentDirectRoom.matrixUser)) onDeselectUser(recentDirectRoom.matrixUser) } else { - state.eventSink(UserListEvents.AddToSelection(recentDirectRoom.matrixUser)) + state.eventSink(UserListEvent.AddToSelection(recentDirectRoom.matrixUser)) onSelectUser(recentDirectRoom.matrixUser) } }, diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/DefaultUserListPresenter.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/DefaultUserListPresenter.kt index 286f0abe68..adbab19180 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/DefaultUserListPresenter.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/DefaultUserListPresenter.kt @@ -85,11 +85,11 @@ class DefaultUserListPresenter( }.launchIn(this) } - fun handleEvent(event: UserListEvents) { + fun handleEvent(event: UserListEvent) { when (event) { - is UserListEvents.OnSearchActiveChanged -> isSearchActive = event.active - is UserListEvents.AddToSelection -> userListDataStore.selectUser(event.matrixUser) - is UserListEvents.RemoveFromSelection -> userListDataStore.removeUserFromSelection(event.matrixUser) + is UserListEvent.OnSearchActiveChanged -> isSearchActive = event.active + is UserListEvent.AddToSelection -> userListDataStore.selectUser(event.matrixUser) + is UserListEvent.RemoveFromSelection -> userListDataStore.removeUserFromSelection(event.matrixUser) } } diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListEvents.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListEvent.kt similarity index 88% rename from features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListEvents.kt rename to features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListEvent.kt index f7601f6d36..098e4411d2 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListEvents.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListEvent.kt @@ -10,8 +10,8 @@ package io.element.android.features.startchat.impl.userlist import io.element.android.libraries.matrix.api.user.MatrixUser -sealed interface UserListEvents { - data class AddToSelection(val matrixUser: MatrixUser) : UserListEvents - data class RemoveFromSelection(val matrixUser: MatrixUser) : UserListEvents - data class OnSearchActiveChanged(val active: Boolean) : UserListEvents +sealed interface UserListEvent { + data class AddToSelection(val matrixUser: MatrixUser) : UserListEvent + data class RemoveFromSelection(val matrixUser: MatrixUser) : UserListEvent + data class OnSearchActiveChanged(val active: Boolean) : UserListEvent } diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListState.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListState.kt index 6a507f8a75..6ce5bafddf 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListState.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListState.kt @@ -23,7 +23,7 @@ data class UserListState( val isSearchActive: Boolean, val selectionMode: SelectionMode, val recentDirectRooms: ImmutableList, - val eventSink: (UserListEvents) -> Unit, + val eventSink: (UserListEvent) -> Unit, ) { val isMultiSelectionEnabled = selectionMode == SelectionMode.Multiple } diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListStatePreviewParam.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListStatePreviewParam.kt index 190ff23ad2..a84927be62 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListStatePreviewParam.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/UserListStatePreviewParam.kt @@ -68,7 +68,7 @@ fun aUserListState( showSearchLoader: Boolean = false, selectionMode: SelectionMode = SelectionMode.Single, recentDirectRooms: List = emptyList(), - eventSink: (UserListEvents) -> Unit = {}, + eventSink: (UserListEvent) -> Unit = {}, ) = UserListState( searchQuery = TextFieldState(initialText = searchQuery), isSearchActive = isSearchActive, diff --git a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/userlist/DefaultUserListPresenterTest.kt b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/userlist/DefaultUserListPresenterTest.kt index 55fd30a84e..0366ec79bd 100644 --- a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/userlist/DefaultUserListPresenterTest.kt +++ b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/userlist/DefaultUserListPresenterTest.kt @@ -80,7 +80,7 @@ class DefaultUserListPresenterTest { presenter.test { val initialState = awaitItem() - initialState.eventSink(UserListEvents.OnSearchActiveChanged(true)) + initialState.eventSink(UserListEvent.OnSearchActiveChanged(true)) assertThat(awaitItem().isSearchActive).isTrue() val matrixIdQuery = "@name:matrix.org" @@ -93,7 +93,7 @@ class DefaultUserListPresenterTest { assertThat(awaitItem().searchQuery.text.toString()).isEqualTo(notMatrixIdQuery) assertThat(userRepository.providedQuery).isEqualTo(notMatrixIdQuery) - initialState.eventSink(UserListEvents.OnSearchActiveChanged(false)) + initialState.eventSink(UserListEvent.OnSearchActiveChanged(false)) assertThat(awaitItem().isSearchActive).isFalse() } } @@ -190,22 +190,22 @@ class DefaultUserListPresenterTest { val userABis = aMatrixUser("@userA:domain", "A") val userC = aMatrixUser("@userC:domain", "C") - initialState.eventSink(UserListEvents.AddToSelection(userA)) + initialState.eventSink(UserListEvent.AddToSelection(userA)) assertThat(awaitItem().selectedUsers).containsExactly(userA) - initialState.eventSink(UserListEvents.AddToSelection(userB)) + initialState.eventSink(UserListEvent.AddToSelection(userB)) assertThat(awaitItem().selectedUsers).containsExactly(userA, userB) - initialState.eventSink(UserListEvents.AddToSelection(userABis)) - initialState.eventSink(UserListEvents.AddToSelection(userC)) + initialState.eventSink(UserListEvent.AddToSelection(userABis)) + initialState.eventSink(UserListEvent.AddToSelection(userC)) // duplicated users should be ignored assertThat(awaitItem().selectedUsers).containsExactly(userA, userB, userC) - initialState.eventSink(UserListEvents.RemoveFromSelection(userB)) + initialState.eventSink(UserListEvent.RemoveFromSelection(userB)) assertThat(awaitItem().selectedUsers).containsExactly(userA, userC) - initialState.eventSink(UserListEvents.RemoveFromSelection(userA)) + initialState.eventSink(UserListEvent.RemoveFromSelection(userA)) assertThat(awaitItem().selectedUsers).containsExactly(userC) - initialState.eventSink(UserListEvents.RemoveFromSelection(userC)) + initialState.eventSink(UserListEvent.RemoveFromSelection(userC)) assertThat(awaitItem().selectedUsers).isEmpty() } } From 650f20d6ba5403fe071962b6bab9acd4abf04a9b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:28:49 +0200 Subject: [PATCH 065/112] `UserProfileEvents` -> `UserProfileEvent` --- .../details/RoomMemberDetailsPresenter.kt | 8 ++--- .../details/RoomMemberDetailsPresenterTest.kt | 4 +-- ...erProfileEvents.kt => UserProfileEvent.kt} | 18 +++++----- .../userprofile/api/UserProfileState.kt | 2 +- .../impl/root/UserProfilePresenter.kt | 20 +++++------ .../impl/UserProfilePresenterTest.kt | 36 +++++++++---------- .../shared/UserProfileStatePreviewParam.kt | 4 +-- .../userprofile/shared/UserProfileView.kt | 16 ++++----- .../shared/blockuser/BlockUserDialogs.kt | 10 +++--- .../shared/blockuser/BlockUserSection.kt | 16 ++++----- .../userprofile/UserProfileViewTest.kt | 30 ++++++++-------- .../shared/blockuser/BlockUserDialogsTest.kt | 18 +++++----- 12 files changed, 91 insertions(+), 91 deletions(-) rename features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/{UserProfileEvents.kt => UserProfileEvent.kt} (57%) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt index 9b18c88c93..85eca4fbae 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt @@ -17,7 +17,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject -import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileEvent import io.element.android.features.userprofile.api.UserProfilePresenterFactory import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileVerificationState @@ -112,12 +112,12 @@ class RoomMemberDetailsPresenter( } } - fun handleEvent(event: UserProfileEvents) { + fun handleEvent(event: UserProfileEvent) { when (event) { - UserProfileEvents.WithdrawVerification -> coroutineScope.launch { + UserProfileEvent.WithdrawVerification -> coroutineScope.launch { encryptionService.withdrawVerification(roomMemberId) } - is UserProfileEvents.CopyToClipboard -> { + is UserProfileEvent.CopyToClipboard -> { clipboardHelper.copyPlainText(event.text) snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard)) } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenterTest.kt index 34ac8d623f..53ec3b1491 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenterTest.kt @@ -11,7 +11,7 @@ package io.element.android.features.roomdetails.impl.members.details import com.google.common.truth.Truth.assertThat import io.element.android.features.roomdetails.impl.aJoinedRoom import io.element.android.features.roomdetails.impl.members.aRoomMember -import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileEvent import io.element.android.features.userprofile.api.UserProfilePresenterFactory import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.shared.aUserProfileState @@ -348,7 +348,7 @@ class RoomMemberDetailsPresenterTest { consumeItemsUntilPredicate { it.verificationState == UserProfileVerificationState.VERIFICATION_VIOLATION } - initialState.eventSink(UserProfileEvents.WithdrawVerification) + initialState.eventSink(UserProfileEvent.WithdrawVerification) withdrawVerificationResult.assertions().isCalledOnce() } } diff --git a/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEvents.kt b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEvent.kt similarity index 57% rename from features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEvents.kt rename to features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEvent.kt index 55c9c40f74..24265f0d9b 100644 --- a/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEvents.kt +++ b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEvent.kt @@ -8,13 +8,13 @@ package io.element.android.features.userprofile.api -sealed interface UserProfileEvents { - data object StartDM : UserProfileEvents - data object ClearStartDMState : UserProfileEvents - data class BlockUser(val needsConfirmation: Boolean = false) : UserProfileEvents - data class UnblockUser(val needsConfirmation: Boolean = false) : UserProfileEvents - data object ClearBlockUserError : UserProfileEvents - data object ClearConfirmationDialog : UserProfileEvents - data object WithdrawVerification : UserProfileEvents - data class CopyToClipboard(val text: String) : UserProfileEvents +sealed interface UserProfileEvent { + data object StartDM : UserProfileEvent + data object ClearStartDMState : UserProfileEvent + data class BlockUser(val needsConfirmation: Boolean = false) : UserProfileEvent + data class UnblockUser(val needsConfirmation: Boolean = false) : UserProfileEvent + data object ClearBlockUserError : UserProfileEvent + data object ClearConfirmationDialog : UserProfileEvent + data object WithdrawVerification : UserProfileEvent + data class CopyToClipboard(val text: String) : UserProfileEvent } diff --git a/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileState.kt b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileState.kt index 4de0d50ca4..bdc1c5a5c7 100644 --- a/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileState.kt +++ b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileState.kt @@ -28,7 +28,7 @@ data class UserProfileState( val canCall: Boolean, val snackbarMessage: SnackbarMessage?, val displayedStatus: DisplayedStatus?, - val eventSink: (UserProfileEvents) -> Unit + val eventSink: (UserProfileEvent) -> Unit ) { enum class ConfirmationDialog { Block, diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt index 24977713c0..08251981c9 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt @@ -23,7 +23,7 @@ import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject import io.element.android.features.enterprise.api.SessionEnterpriseService import io.element.android.features.startchat.api.StartDMAction -import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileEvent import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileState.ConfirmationDialog import io.element.android.features.userprofile.api.UserProfileVerificationState @@ -101,9 +101,9 @@ class UserProfilePresenter( } val userProfile by produceState(null) { value = client.getProfile(userId).getOrNull() } - fun handleEvent(event: UserProfileEvents) { + fun handleEvent(event: UserProfileEvent) { when (event) { - is UserProfileEvents.BlockUser -> { + is UserProfileEvent.BlockUser -> { if (event.needsConfirmation) { confirmationDialog = ConfirmationDialog.Block } else { @@ -111,7 +111,7 @@ class UserProfilePresenter( coroutineScope.blockUser(isBlocked) } } - is UserProfileEvents.UnblockUser -> { + is UserProfileEvent.UnblockUser -> { if (event.needsConfirmation) { confirmationDialog = ConfirmationDialog.Unblock } else { @@ -119,11 +119,11 @@ class UserProfilePresenter( coroutineScope.unblockUser(isBlocked) } } - UserProfileEvents.ClearConfirmationDialog -> confirmationDialog = null - UserProfileEvents.ClearBlockUserError -> { + UserProfileEvent.ClearConfirmationDialog -> confirmationDialog = null + UserProfileEvent.ClearBlockUserError -> { isBlocked.value = AsyncData.Success(isBlocked.value.dataOrNull().orFalse()) } - UserProfileEvents.StartDM -> { + UserProfileEvent.StartDM -> { coroutineScope.launch { startDMAction.execute( matrixUser = userProfile ?: MatrixUser(userId), @@ -132,12 +132,12 @@ class UserProfilePresenter( ) } } - UserProfileEvents.ClearStartDMState -> { + UserProfileEvent.ClearStartDMState -> { startDmActionState.value = AsyncAction.Uninitialized } // Do nothing for other event as they are handled by the RoomMemberDetailsPresenter if needed - UserProfileEvents.WithdrawVerification, - is UserProfileEvents.CopyToClipboard -> Unit + UserProfileEvent.WithdrawVerification, + is UserProfileEvent.CopyToClipboard -> Unit } } diff --git a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt index bbdc698f17..57a7ae273f 100644 --- a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt +++ b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt @@ -18,7 +18,7 @@ import io.element.android.features.enterprise.test.FakeSessionEnterpriseService import io.element.android.features.invitepeople.test.FakeStartDMAction import io.element.android.features.startchat.api.ConfirmingStartDmWithMatrixUser import io.element.android.features.startchat.api.StartDMAction -import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileEvent import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.impl.root.UserProfilePresenter @@ -182,12 +182,12 @@ class UserProfilePresenterTest { val presenter = createUserProfilePresenter() presenter.test { val initialState = awaitFirstItem() - initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = true)) + initialState.eventSink(UserProfileEvent.BlockUser(needsConfirmation = true)) val dialogState = awaitItem() assertThat(dialogState.displayConfirmationDialog).isEqualTo(UserProfileState.ConfirmationDialog.Block) - dialogState.eventSink(UserProfileEvents.ClearConfirmationDialog) + dialogState.eventSink(UserProfileEvent.ClearConfirmationDialog) assertThat(awaitItem().displayConfirmationDialog).isNull() } } @@ -202,12 +202,12 @@ class UserProfilePresenterTest { ) presenter.test { val initialState = awaitFirstItem() - initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false)) + initialState.eventSink(UserProfileEvent.BlockUser(needsConfirmation = false)) assertThat(awaitItem().isBlocked.isLoading()).isTrue() ignoredUsersFlow.emit(persistentListOf(A_USER_ID)) assertThat(awaitItem().isBlocked.dataOrNull()).isTrue() - initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false)) + initialState.eventSink(UserProfileEvent.UnblockUser(needsConfirmation = false)) assertThat(awaitItem().isBlocked.isLoading()).isTrue() ignoredUsersFlow.emit(persistentListOf()) assertThat(awaitItem().isBlocked.dataOrNull()).isFalse() @@ -222,12 +222,12 @@ class UserProfilePresenterTest { val presenter = createUserProfilePresenter(client = matrixClient) presenter.test { val initialState = awaitFirstItem(count = 2) - initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false)) + initialState.eventSink(UserProfileEvent.BlockUser(needsConfirmation = false)) assertThat(awaitItem().isBlocked.isLoading()).isTrue() val errorState = awaitItem() assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(AN_EXCEPTION) // Clear error - initialState.eventSink(UserProfileEvents.ClearBlockUserError) + initialState.eventSink(UserProfileEvent.ClearBlockUserError) assertThat(awaitItem().isBlocked).isEqualTo(AsyncData.Success(false)) } } @@ -240,12 +240,12 @@ class UserProfilePresenterTest { val presenter = createUserProfilePresenter(client = matrixClient) presenter.test { val initialState = awaitFirstItem(count = 2) - initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false)) + initialState.eventSink(UserProfileEvent.UnblockUser(needsConfirmation = false)) assertThat(awaitItem().isBlocked.isLoading()).isTrue() val errorState = awaitItem() assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(AN_EXCEPTION) // Clear error - initialState.eventSink(UserProfileEvents.ClearBlockUserError) + initialState.eventSink(UserProfileEvent.ClearBlockUserError) assertThat(awaitItem().isBlocked).isEqualTo(AsyncData.Success(true)) } } @@ -255,12 +255,12 @@ class UserProfilePresenterTest { val presenter = createUserProfilePresenter() presenter.test { val initialState = awaitFirstItem() - initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = true)) + initialState.eventSink(UserProfileEvent.UnblockUser(needsConfirmation = true)) val dialogState = awaitItem() assertThat(dialogState.displayConfirmationDialog).isEqualTo(UserProfileState.ConfirmationDialog.Unblock) - dialogState.eventSink(UserProfileEvents.ClearConfirmationDialog) + dialogState.eventSink(UserProfileEvent.ClearConfirmationDialog) assertThat(awaitItem().displayConfirmationDialog).isNull() } } @@ -279,7 +279,7 @@ class UserProfilePresenterTest { val initialState = awaitFirstItem() assertThat(initialState.startDmActionState).isInstanceOf(AsyncAction.Uninitialized::class.java) val matrixUser = MatrixUser(UserId("@alice:server.org")) - initialState.eventSink(UserProfileEvents.StartDM) + initialState.eventSink(UserProfileEvent.StartDM) awaitItem().also { state -> assertThat(state.startDmActionState).isEqualTo(startDMFailureResult) executeResult.assertions().isCalledOnce().with( @@ -287,7 +287,7 @@ class UserProfilePresenterTest { value(false), any(), ) - state.eventSink(UserProfileEvents.ClearStartDMState) + state.eventSink(UserProfileEvent.ClearStartDMState) } awaitItem().also { state -> assertThat(state.startDmActionState.isUninitialized()).isTrue() @@ -309,7 +309,7 @@ class UserProfilePresenterTest { val initialState = awaitFirstItem() assertThat(initialState.startDmActionState).isInstanceOf(AsyncAction.Uninitialized::class.java) val matrixUser = MatrixUser(UserId("@alice:server.org")) - initialState.eventSink(UserProfileEvents.StartDM) + initialState.eventSink(UserProfileEvent.StartDM) awaitItem().also { state -> assertThat(state.startDmActionState).isEqualTo(startDMSuccessResult) executeResult.assertions().isCalledOnce().with( @@ -335,7 +335,7 @@ class UserProfilePresenterTest { }.test { val initialState = awaitFirstItem() assertThat(initialState.startDmActionState).isInstanceOf(AsyncAction.Uninitialized::class.java) - initialState.eventSink(UserProfileEvents.StartDM) + initialState.eventSink(UserProfileEvent.StartDM) val confirmingState = awaitItem() assertThat(confirmingState.startDmActionState).isEqualTo(startDMConfirmationResult) executeResult.assertions().isCalledOnce().with( @@ -344,7 +344,7 @@ class UserProfilePresenterTest { any(), ) // Cancelling should not create the DM - confirmingState.eventSink(UserProfileEvents.ClearStartDMState) + confirmingState.eventSink(UserProfileEvent.ClearStartDMState) val finalState = awaitItem() assertThat(finalState.startDmActionState.isUninitialized()).isTrue() executeResult.assertions().isCalledExactly(1) @@ -365,7 +365,7 @@ class UserProfilePresenterTest { }.test { val initialState = awaitFirstItem() assertThat(initialState.startDmActionState).isInstanceOf(AsyncAction.Uninitialized::class.java) - initialState.eventSink(UserProfileEvents.StartDM) + initialState.eventSink(UserProfileEvent.StartDM) val confirmingState = awaitItem() assertThat(confirmingState.startDmActionState).isEqualTo(startDMConfirmationResult) executeResult.assertions().isCalledOnce().with( @@ -374,7 +374,7 @@ class UserProfilePresenterTest { any(), ) // Start DM again should invoke the action with createIfDmDoesNotExist = true - confirmingState.eventSink(UserProfileEvents.StartDM) + confirmingState.eventSink(UserProfileEvent.StartDM) executeResult.assertions().isCalledExactly(2).withSequence( listOf(value(matrixUser), value(false), any()), listOf(value(matrixUser), value(true), any()), diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStatePreviewParam.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStatePreviewParam.kt index 84a95e0b14..bfb3df4ac6 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStatePreviewParam.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStatePreviewParam.kt @@ -10,7 +10,7 @@ package io.element.android.features.userprofile.shared import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.startchat.api.ConfirmingStartDmWithMatrixUser -import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileEvent import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.libraries.architecture.AsyncAction @@ -50,7 +50,7 @@ fun aUserProfileState( canCall: Boolean = false, snackbarMessage: SnackbarMessage? = null, displayedStatus: DisplayedStatus? = null, - eventSink: (UserProfileEvents) -> Unit = {}, + eventSink: (UserProfileEvent) -> Unit = {}, ) = UserProfileState( userId = userId, userName = userName, diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt index b9f43db36d..643db456b1 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt @@ -23,7 +23,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.startchat.api.ConfirmingStartDmWithMatrixUser -import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileEvent import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.shared.blockuser.BlockUserDialogs @@ -83,15 +83,15 @@ fun UserProfileView( openAvatarPreview(state.userName ?: state.userId.value, avatarUrl) }, onUserIdClick = { - state.eventSink(UserProfileEvents.CopyToClipboard(state.userId.value)) + state.eventSink(UserProfileEvent.CopyToClipboard(state.userId.value)) }, - withdrawVerificationClick = { state.eventSink(UserProfileEvents.WithdrawVerification) }, + withdrawVerificationClick = { state.eventSink(UserProfileEvent.WithdrawVerification) }, ) UserProfileMainActionsSection( isCurrentUser = state.isCurrentUser, canCall = state.canCall, onShareUser = onShareUser, - onStartDM = { state.eventSink(UserProfileEvents.StartDM) }, + onStartDM = { state.eventSink(UserProfileEvent.StartDM) }, onCall = { intent -> state.dmRoomId?.let { onStartCall(it, intent) } } ) Spacer(modifier = Modifier.height(26.dp)) @@ -109,18 +109,18 @@ fun UserProfileView( }, onSuccess = onOpenDm, errorMessage = { stringResource(R.string.screen_start_chat_error_starting_chat) }, - onRetry = { state.eventSink(UserProfileEvents.StartDM) }, - onErrorDismiss = { state.eventSink(UserProfileEvents.ClearStartDMState) }, + onRetry = { state.eventSink(UserProfileEvent.StartDM) }, + onErrorDismiss = { state.eventSink(UserProfileEvent.ClearStartDMState) }, confirmationDialog = { data -> if (data is ConfirmingStartDmWithMatrixUser) { CreateDmConfirmationBottomSheet( matrixUser = data.matrixUser, isUserIdentityUnknown = data.isUserIdentityUnknown, onSendInvite = { - state.eventSink(UserProfileEvents.StartDM) + state.eventSink(UserProfileEvent.StartDM) }, onDismiss = { - state.eventSink(UserProfileEvents.ClearStartDMState) + state.eventSink(UserProfileEvent.ClearStartDMState) }, ) } diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogs.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogs.kt index 2ba17b947b..220118c5a7 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogs.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogs.kt @@ -10,7 +10,7 @@ package io.element.android.features.userprofile.shared.blockuser import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource -import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileEvent import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.shared.R import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog @@ -23,24 +23,24 @@ fun BlockUserDialogs(state: UserProfileState) { BlockConfirmationDialog( onBlockAction = { state.eventSink( - UserProfileEvents.BlockUser( + UserProfileEvent.BlockUser( needsConfirmation = false ) ) }, - onDismiss = { state.eventSink(UserProfileEvents.ClearConfirmationDialog) } + onDismiss = { state.eventSink(UserProfileEvent.ClearConfirmationDialog) } ) } UserProfileState.ConfirmationDialog.Unblock -> { UnblockConfirmationDialog( onUnblockAction = { state.eventSink( - UserProfileEvents.UnblockUser( + UserProfileEvent.UnblockUser( needsConfirmation = false ) ) }, - onDismiss = { state.eventSink(UserProfileEvents.ClearConfirmationDialog) } + onDismiss = { state.eventSink(UserProfileEvent.ClearConfirmationDialog) } ) } } diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserSection.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserSection.kt index 30f8c28607..e8210997b0 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserSection.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserSection.kt @@ -15,7 +15,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileEvent import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.shared.R import io.element.android.libraries.architecture.AsyncData @@ -50,13 +50,13 @@ fun BlockUserSection( if (isBlocked is AsyncData.Failure) { RetryDialog( content = stringResource(CommonStrings.error_unknown), - onDismiss = { state.eventSink(UserProfileEvents.ClearBlockUserError) }, + onDismiss = { state.eventSink(UserProfileEvent.ClearBlockUserError) }, onRetry = { val event = when (isBlocked.prevData) { - true -> UserProfileEvents.UnblockUser(needsConfirmation = false) - false -> UserProfileEvents.BlockUser(needsConfirmation = false) + true -> UserProfileEvent.UnblockUser(needsConfirmation = false) + false -> UserProfileEvent.BlockUser(needsConfirmation = false) // null case Should not happen - null -> UserProfileEvents.ClearBlockUserError + null -> UserProfileEvent.ClearBlockUserError } state.eventSink(event) }, @@ -68,7 +68,7 @@ fun BlockUserSection( private fun PreferenceBlockUser( isBlocked: Boolean?, isLoading: Boolean, - eventSink: (UserProfileEvents) -> Unit, + eventSink: (UserProfileEvent) -> Unit, ) { val loadingCurrentValue = @Composable { _: Boolean -> CircularProgressIndicator( @@ -82,7 +82,7 @@ private fun PreferenceBlockUser( ListItem( content = { Text(stringResource(R.string.screen_dm_details_unblock_user)) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())), - onClick = { if (!isLoading) eventSink(UserProfileEvents.UnblockUser(needsConfirmation = true)) }, + onClick = { if (!isLoading) eventSink(UserProfileEvent.UnblockUser(needsConfirmation = true)) }, trailingContent = if (isLoading) ListItemContent.Custom(loadingCurrentValue) else null, ) } else { @@ -90,7 +90,7 @@ private fun PreferenceBlockUser( content = { Text(stringResource(R.string.screen_dm_details_block_user)) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())), style = ListItemStyle.Destructive, - onClick = { if (!isLoading) eventSink(UserProfileEvents.BlockUser(needsConfirmation = true)) }, + onClick = { if (!isLoading) eventSink(UserProfileEvent.BlockUser(needsConfirmation = true)) }, trailingContent = if (isLoading) ListItemContent.Custom(loadingCurrentValue) else null, ) } diff --git a/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt b/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt index 63930affee..9c6aa66670 100644 --- a/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt +++ b/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt @@ -16,7 +16,7 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.performClick import androidx.compose.ui.test.v2.runAndroidComposeUiTest -import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileEvent import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.shared.R @@ -89,7 +89,7 @@ class UserProfileViewTest : RobolectricTest() { @Test fun `on Message clicked - the StartDm event is emitted`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setUserProfileView( state = aUserProfileState( dmRoomId = A_ROOM_ID, @@ -97,7 +97,7 @@ class UserProfileViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_message) - eventsRecorder.assertSingle(UserProfileEvents.StartDM) + eventsRecorder.assertSingle(UserProfileEvent.StartDM) } @Test @@ -131,19 +131,19 @@ class UserProfileViewTest : RobolectricTest() { @Config(qualifiers = "h1024dp") @Test fun `on Block user clicked - a BlockUser event is emitted with needsConfirmation`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setUserProfileView( state = aUserProfileState( eventSink = eventsRecorder, ), ) clickOn(R.string.screen_dm_details_block_user) - eventsRecorder.assertSingle(UserProfileEvents.BlockUser(needsConfirmation = true)) + eventsRecorder.assertSingle(UserProfileEvent.BlockUser(needsConfirmation = true)) } @Test fun `on confirming block user - a BlockUser event is emitted without needsConfirmation`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setUserProfileView( state = aUserProfileState( displayConfirmationDialog = UserProfileState.ConfirmationDialog.Block, @@ -151,12 +151,12 @@ class UserProfileViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_dm_details_block_alert_action) - eventsRecorder.assertSingle(UserProfileEvents.BlockUser(needsConfirmation = false)) + eventsRecorder.assertSingle(UserProfileEvent.BlockUser(needsConfirmation = false)) } @Test fun `on canceling blocking a user - a ClearConfirmationDialog event is emitted`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setUserProfileView( state = aUserProfileState( displayConfirmationDialog = UserProfileState.ConfirmationDialog.Block, @@ -164,13 +164,13 @@ class UserProfileViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_cancel) - eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog) + eventsRecorder.assertSingle(UserProfileEvent.ClearConfirmationDialog) } @Config(qualifiers = "h1024dp") @Test fun `on Unblock user clicked - an UnblockUser event is emitted with needsConfirmation`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setUserProfileView( state = aUserProfileState( isBlocked = AsyncData.Success(true), @@ -178,12 +178,12 @@ class UserProfileViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_dm_details_unblock_user) - eventsRecorder.assertSingle(UserProfileEvents.UnblockUser(needsConfirmation = true)) + eventsRecorder.assertSingle(UserProfileEvent.UnblockUser(needsConfirmation = true)) } @Test fun `on confirming Unblock user - an UnblockUser event is emitted without needsConfirmation`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setUserProfileView( state = aUserProfileState( isBlocked = AsyncData.Success(true), @@ -192,12 +192,12 @@ class UserProfileViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_dm_details_unblock_alert_action) - eventsRecorder.assertSingle(UserProfileEvents.UnblockUser(needsConfirmation = false)) + eventsRecorder.assertSingle(UserProfileEvent.UnblockUser(needsConfirmation = false)) } @Test fun `on canceling unblocking a user - a ClearConfirmationDialog event is emitted`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setUserProfileView( state = aUserProfileState( isBlocked = AsyncData.Success(true), @@ -206,7 +206,7 @@ class UserProfileViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_cancel) - eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog) + eventsRecorder.assertSingle(UserProfileEvent.ClearConfirmationDialog) } @Test diff --git a/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogsTest.kt b/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogsTest.kt index b0a2590e8a..575c9ff7a0 100644 --- a/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogsTest.kt +++ b/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogsTest.kt @@ -12,7 +12,7 @@ package io.element.android.features.userprofile.shared.blockuser import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.v2.runAndroidComposeUiTest -import io.element.android.features.userprofile.api.UserProfileEvents +import io.element.android.features.userprofile.api.UserProfileEvent import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.shared.R import io.element.android.features.userprofile.shared.aUserProfileState @@ -25,7 +25,7 @@ import org.junit.Test class BlockUserDialogsTest : RobolectricTest() { @Test fun `confirm block user emit expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setContent { BlockUserDialogs( state = aUserProfileState( @@ -35,12 +35,12 @@ class BlockUserDialogsTest : RobolectricTest() { ) } clickOn(R.string.screen_dm_details_block_alert_action) - eventsRecorder.assertSingle(UserProfileEvents.BlockUser(false)) + eventsRecorder.assertSingle(UserProfileEvent.BlockUser(false)) } @Test fun `cancel block user emit expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setContent { BlockUserDialogs( state = aUserProfileState( @@ -50,12 +50,12 @@ class BlockUserDialogsTest : RobolectricTest() { ) } clickOn(CommonStrings.action_cancel) - eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog) + eventsRecorder.assertSingle(UserProfileEvent.ClearConfirmationDialog) } @Test fun `confirm unblock user emit expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setContent { BlockUserDialogs( state = aUserProfileState( @@ -65,12 +65,12 @@ class BlockUserDialogsTest : RobolectricTest() { ) } clickOn(R.string.screen_dm_details_unblock_alert_action) - eventsRecorder.assertSingle(UserProfileEvents.UnblockUser(false)) + eventsRecorder.assertSingle(UserProfileEvent.UnblockUser(false)) } @Test fun `cancel unblock user emit expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setContent { BlockUserDialogs( state = aUserProfileState( @@ -80,6 +80,6 @@ class BlockUserDialogsTest : RobolectricTest() { ) } clickOn(CommonStrings.action_cancel) - eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog) + eventsRecorder.assertSingle(UserProfileEvent.ClearConfirmationDialog) } } From 2dda3b807db36b95e33a87ff3c35fbdeace3fbf5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:29:30 +0200 Subject: [PATCH 066/112] `IncomingVerificationViewEvents` -> `IncomingVerificationViewEvent` --- .../incoming/IncomingVerificationPresenter.kt | 12 ++--- .../incoming/IncomingVerificationState.kt | 2 +- .../IncomingVerificationStatePreviewParam.kt | 2 +- .../impl/incoming/IncomingVerificationView.kt | 14 +++--- ...ts.kt => IncomingVerificationViewEvent.kt} | 12 ++--- .../IncomingVerificationPresenterTest.kt | 14 +++--- .../incoming/IncomingVerificationViewTest.kt | 48 +++++++++---------- 7 files changed, 52 insertions(+), 52 deletions(-) rename features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/{IncomingVerificationViewEvents.kt => IncomingVerificationViewEvent.kt} (82%) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenter.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenter.kt index aa991a54bc..4cde3f927f 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenter.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenter.kt @@ -108,18 +108,18 @@ class IncomingVerificationPresenter( } } - fun handleEvent(event: IncomingVerificationViewEvents) { + fun handleEvent(event: IncomingVerificationViewEvent) { Timber.d("Verification user action: ${event::class.simpleName}") when (event) { - IncomingVerificationViewEvents.StartVerification -> + IncomingVerificationViewEvent.StartVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.AcceptIncomingRequest) - IncomingVerificationViewEvents.IgnoreVerification -> + IncomingVerificationViewEvent.IgnoreVerification -> navigator.onFinish() - IncomingVerificationViewEvents.ConfirmVerification -> + IncomingVerificationViewEvent.ConfirmVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.AcceptChallenge) - IncomingVerificationViewEvents.DeclineVerification -> + IncomingVerificationViewEvent.DeclineVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.DeclineChallenge) - IncomingVerificationViewEvents.GoBack -> { + IncomingVerificationViewEvent.GoBack -> { when (val verificationStep = step) { is Step.Initial -> if (verificationStep.isWaiting) { stateAndDispatch.dispatchAction(StateMachineEvent.Cancel) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationState.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationState.kt index 311fc13e74..e3663800b2 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationState.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationState.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.matrix.api.verification.VerificationRequest data class IncomingVerificationState( val step: Step, val request: VerificationRequest.Incoming, - val eventSink: (IncomingVerificationViewEvents) -> Unit, + val eventSink: (IncomingVerificationViewEvent) -> Unit, ) { @Stable sealed interface Step { diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStatePreviewParam.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStatePreviewParam.kt index c8d70e03c0..68672be025 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStatePreviewParam.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStatePreviewParam.kt @@ -87,7 +87,7 @@ internal fun anIncomingUserVerificationRequest() = VerificationRequest.Incoming. internal fun anIncomingVerificationState( step: Step = aStepInitial(), verificationRequest: VerificationRequest.Incoming = anIncomingSessionVerificationRequest(), - eventSink: (IncomingVerificationViewEvents) -> Unit = {}, + eventSink: (IncomingVerificationViewEvent) -> Unit = {}, ) = IncomingVerificationState( step = step, request = verificationRequest, diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt index 64adc78aa0..00b430fb53 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt @@ -62,7 +62,7 @@ fun IncomingVerificationView( val step = state.step BackHandler { - state.eventSink(IncomingVerificationViewEvents.GoBack) + state.eventSink(IncomingVerificationViewEvent.GoBack) } HeaderFooterPage( modifier = modifier, @@ -70,7 +70,7 @@ fun IncomingVerificationView( TopAppBar( title = {}, navigationIcon = { - BackButton(onClick = { state.eventSink(IncomingVerificationViewEvents.GoBack) }) + BackButton(onClick = { state.eventSink(IncomingVerificationViewEvent.GoBack) }) }, colors = topAppBarColors(containerColor = Color.Transparent), ) @@ -217,13 +217,13 @@ private fun IncomingVerificationBottomMenu( text = stringResource(CommonStrings.action_start_verification), enabled = !step.isWaiting, showProgress = step.isWaiting, - onClick = { eventSink(IncomingVerificationViewEvents.StartVerification) }, + onClick = { eventSink(IncomingVerificationViewEvent.StartVerification) }, ) TextButton( modifier = Modifier.fillMaxWidth(), text = stringResource(CommonStrings.action_ignore), enabled = !step.isWaiting, - onClick = { eventSink(IncomingVerificationViewEvents.IgnoreVerification) }, + onClick = { eventSink(IncomingVerificationViewEvent.IgnoreVerification) }, ) } } @@ -235,14 +235,14 @@ private fun IncomingVerificationBottomMenu( enabled = !step.isWaiting, showProgress = step.isWaiting, onClick = { - eventSink(IncomingVerificationViewEvents.ConfirmVerification) + eventSink(IncomingVerificationViewEvent.ConfirmVerification) }, ) TextButton( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.screen_session_verification_they_dont_match), enabled = !step.isWaiting, - onClick = { eventSink(IncomingVerificationViewEvents.DeclineVerification) }, + onClick = { eventSink(IncomingVerificationViewEvent.DeclineVerification) }, ) } } @@ -254,7 +254,7 @@ private fun IncomingVerificationBottomMenu( modifier = Modifier.fillMaxWidth(), text = stringResource(CommonStrings.action_done), onClick = { - eventSink(IncomingVerificationViewEvents.GoBack) + eventSink(IncomingVerificationViewEvent.GoBack) }, ) } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationViewEvents.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationViewEvent.kt similarity index 82% rename from features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationViewEvents.kt rename to features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationViewEvent.kt index 383354860b..26a05b44cc 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationViewEvents.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationViewEvent.kt @@ -8,10 +8,10 @@ package io.element.android.features.verifysession.impl.incoming -sealed interface IncomingVerificationViewEvents { - data object GoBack : IncomingVerificationViewEvents - data object StartVerification : IncomingVerificationViewEvents - data object IgnoreVerification : IncomingVerificationViewEvents - data object ConfirmVerification : IncomingVerificationViewEvents - data object DeclineVerification : IncomingVerificationViewEvents +sealed interface IncomingVerificationViewEvent { + data object GoBack : IncomingVerificationViewEvent + data object StartVerification : IncomingVerificationViewEvent + data object IgnoreVerification : IncomingVerificationViewEvent + data object ConfirmVerification : IncomingVerificationViewEvent + data object DeclineVerification : IncomingVerificationViewEvent } diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenterTest.kt index a8d3802268..45d5c623c4 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenterTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenterTest.kt @@ -72,7 +72,7 @@ class IncomingVerificationPresenterTest { acknowledgeVerificationRequestLambda.assertions().isCalledOnce().with(value(anIncomingSessionVerificationRequest)) acceptVerificationRequestLambda.assertions().isNeverCalled() // User accept the incoming verification - initialState.eventSink(IncomingVerificationViewEvents.StartVerification) + initialState.eventSink(IncomingVerificationViewEvent.StartVerification) skipItems(1) val initialWaitingState = awaitItem() assertThat((initialWaitingState.step as IncomingVerificationState.Step.Initial).isWaiting).isTrue() @@ -96,7 +96,7 @@ class IncomingVerificationPresenterTest { ) ) // User claims that the emoji matches - emojiState.eventSink(IncomingVerificationViewEvents.ConfirmVerification) + emojiState.eventSink(IncomingVerificationViewEvent.ConfirmVerification) val emojiWaitingItem = awaitItem() assertThat((emojiWaitingItem.step as IncomingVerificationState.Step.Verifying).isWaiting).isTrue() advanceUntilIdle() @@ -141,7 +141,7 @@ class IncomingVerificationPresenterTest { acknowledgeVerificationRequestLambda.assertions().isCalledOnce().with(value(anIncomingSessionVerificationRequest)) acceptVerificationRequestLambda.assertions().isNeverCalled() // User accept the incoming verification - initialState.eventSink(IncomingVerificationViewEvents.StartVerification) + initialState.eventSink(IncomingVerificationViewEvent.StartVerification) skipItems(1) val initialWaitingState = awaitItem() assertThat((initialWaitingState.step as IncomingVerificationState.Step.Initial).isWaiting).isTrue() @@ -159,7 +159,7 @@ class IncomingVerificationPresenterTest { ) val emojiState = awaitItem() // User claims that the emojis do not match - emojiState.eventSink(IncomingVerificationViewEvents.DeclineVerification) + emojiState.eventSink(IncomingVerificationViewEvent.DeclineVerification) val emojiWaitingItem = awaitItem() assertThat((emojiWaitingItem.step as IncomingVerificationState.Step.Verifying).isWaiting).isTrue() advanceUntilIdle() @@ -241,7 +241,7 @@ class IncomingVerificationPresenterTest { acknowledgeVerificationRequestLambda.assertions().isCalledOnce().with(value(anIncomingSessionVerificationRequest)) acceptVerificationRequestLambda.assertions().isNeverCalled() // User accept the incoming verification - initialState.eventSink(IncomingVerificationViewEvents.StartVerification) + initialState.eventSink(IncomingVerificationViewEvent.StartVerification) skipItems(1) val initialWaitingState = awaitItem() assertThat((initialWaitingState.step as IncomingVerificationState.Step.Initial).isWaiting).isTrue() @@ -259,7 +259,7 @@ class IncomingVerificationPresenterTest { ) val emojiState = awaitItem() // User goes back - emojiState.eventSink(IncomingVerificationViewEvents.GoBack) + emojiState.eventSink(IncomingVerificationViewEvent.GoBack) val emojiWaitingItem = awaitItem() assertThat((emojiWaitingItem.step as IncomingVerificationState.Step.Verifying).isWaiting).isTrue() advanceUntilIdle() @@ -289,7 +289,7 @@ class IncomingVerificationPresenterTest { navigator = IncomingVerificationNavigator(navigatorLambda), ).test { val initialState = awaitItem() - initialState.eventSink(IncomingVerificationViewEvents.IgnoreVerification) + initialState.eventSink(IncomingVerificationViewEvent.IgnoreVerification) skipItems(1) navigatorLambda.assertions().isCalledOnce() } diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationViewTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationViewTest.kt index 5947d1d0a5..c5c64585bc 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationViewTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationViewTest.kt @@ -27,7 +27,7 @@ class IncomingVerificationViewTest : RobolectricTest() { // region step Initial @Test fun `back key pressed - ignore the verification`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setIncomingVerificationView( anIncomingVerificationState( step = aStepInitial(), @@ -35,12 +35,12 @@ class IncomingVerificationViewTest : RobolectricTest() { ), ) pressBackKey() - eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) + eventsRecorder.assertSingle(IncomingVerificationViewEvent.GoBack) } @Test fun `ignore incoming verification emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setIncomingVerificationView( anIncomingVerificationState( step = aStepInitial(), @@ -48,12 +48,12 @@ class IncomingVerificationViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_ignore) - eventsRecorder.assertSingle(IncomingVerificationViewEvents.IgnoreVerification) + eventsRecorder.assertSingle(IncomingVerificationViewEvent.IgnoreVerification) } @Test fun `start incoming verification emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setIncomingVerificationView( anIncomingVerificationState( step = aStepInitial(), @@ -61,12 +61,12 @@ class IncomingVerificationViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_start_verification) - eventsRecorder.assertSingle(IncomingVerificationViewEvents.StartVerification) + eventsRecorder.assertSingle(IncomingVerificationViewEvent.StartVerification) } @Test fun `back key pressed - when awaiting response cancels the verification`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setIncomingVerificationView( anIncomingVerificationState( step = aStepInitial( @@ -76,14 +76,14 @@ class IncomingVerificationViewTest : RobolectricTest() { ), ) pressBackKey() - eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) + eventsRecorder.assertSingle(IncomingVerificationViewEvent.GoBack) } // endregion step Initial // region step Verifying @Test fun `back key pressed - when ready to verify cancels the verification`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setIncomingVerificationView( anIncomingVerificationState( step = IncomingVerificationState.Step.Verifying( @@ -94,12 +94,12 @@ class IncomingVerificationViewTest : RobolectricTest() { ), ) pressBackKey() - eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) + eventsRecorder.assertSingle(IncomingVerificationViewEvent.GoBack) } @Test fun `back key pressed - when verifying and loading emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setIncomingVerificationView( anIncomingVerificationState( step = IncomingVerificationState.Step.Verifying( @@ -110,12 +110,12 @@ class IncomingVerificationViewTest : RobolectricTest() { ), ) pressBackKey() - eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) + eventsRecorder.assertSingle(IncomingVerificationViewEvent.GoBack) } @Test fun `clicking on they do not match emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setIncomingVerificationView( anIncomingVerificationState( step = IncomingVerificationState.Step.Verifying( @@ -126,12 +126,12 @@ class IncomingVerificationViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_session_verification_they_dont_match) - eventsRecorder.assertSingle(IncomingVerificationViewEvents.DeclineVerification) + eventsRecorder.assertSingle(IncomingVerificationViewEvent.DeclineVerification) } @Test fun `clicking on they match emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setIncomingVerificationView( anIncomingVerificationState( step = IncomingVerificationState.Step.Verifying( @@ -142,14 +142,14 @@ class IncomingVerificationViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_session_verification_they_match) - eventsRecorder.assertSingle(IncomingVerificationViewEvents.ConfirmVerification) + eventsRecorder.assertSingle(IncomingVerificationViewEvent.ConfirmVerification) } // endregion // region step Failure @Test fun `back key pressed - when failure resets the flow`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setIncomingVerificationView( anIncomingVerificationState( step = IncomingVerificationState.Step.Failure, @@ -157,12 +157,12 @@ class IncomingVerificationViewTest : RobolectricTest() { ), ) pressBackKey() - eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) + eventsRecorder.assertSingle(IncomingVerificationViewEvent.GoBack) } @Test fun `click on done - when failure resets the flow`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setIncomingVerificationView( anIncomingVerificationState( step = IncomingVerificationState.Step.Failure, @@ -170,7 +170,7 @@ class IncomingVerificationViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_done) - eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) + eventsRecorder.assertSingle(IncomingVerificationViewEvent.GoBack) } // endregion @@ -178,7 +178,7 @@ class IncomingVerificationViewTest : RobolectricTest() { // region step Completed @Test fun `back key pressed - on Completed step emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setIncomingVerificationView( anIncomingVerificationState( step = IncomingVerificationState.Step.Completed, @@ -186,12 +186,12 @@ class IncomingVerificationViewTest : RobolectricTest() { ), ) pressBackKey() - eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) + eventsRecorder.assertSingle(IncomingVerificationViewEvent.GoBack) } @Test fun `when flow is completed and the user clicks on the done button, the expected event is emitted`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setIncomingVerificationView( anIncomingVerificationState( step = IncomingVerificationState.Step.Completed, @@ -199,7 +199,7 @@ class IncomingVerificationViewTest : RobolectricTest() { ), ) clickOn(CommonStrings.action_done) - eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) + eventsRecorder.assertSingle(IncomingVerificationViewEvent.GoBack) } // endregion From 328d2d1260f4e8f8fda949babb792b42f4522871 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:30:19 +0200 Subject: [PATCH 067/112] `OutgoingVerificationViewEvents` -> `OutgoingVerificationViewEvent` --- .../outgoing/OutgoingVerificationPresenter.kt | 14 +++++----- .../outgoing/OutgoingVerificationState.kt | 2 +- .../OutgoingVerificationStatePreviewParam.kt | 2 +- .../impl/outgoing/OutgoingVerificationView.kt | 14 +++++----- ...ts.kt => OutgoingVerificationViewEvent.kt} | 14 +++++----- .../OutgoingVerificationPresenterTest.kt | 24 ++++++++-------- .../outgoing/OutgoingVerificationViewTest.kt | 28 +++++++++---------- 7 files changed, 49 insertions(+), 49 deletions(-) rename features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/{OutgoingVerificationViewEvents.kt => OutgoingVerificationViewEvent.kt} (75%) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenter.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenter.kt index 90e8a3e598..5ecc500725 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenter.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenter.kt @@ -93,16 +93,16 @@ class OutgoingVerificationPresenter( observeVerificationService() } - fun handleEvent(event: OutgoingVerificationViewEvents) { + fun handleEvent(event: OutgoingVerificationViewEvent) { Timber.d("Verification user action: ${event::class.simpleName}") when (event) { // Just relay the event to the state machine - OutgoingVerificationViewEvents.RequestVerification -> StateMachineEvent.RequestVerification(verificationRequest) - OutgoingVerificationViewEvents.StartSasVerification -> StateMachineEvent.StartSasVerification - OutgoingVerificationViewEvents.ConfirmVerification -> StateMachineEvent.AcceptChallenge - OutgoingVerificationViewEvents.DeclineVerification -> StateMachineEvent.DeclineChallenge - OutgoingVerificationViewEvents.Cancel -> StateMachineEvent.Cancel - OutgoingVerificationViewEvents.Reset -> StateMachineEvent.Reset + OutgoingVerificationViewEvent.RequestVerification -> StateMachineEvent.RequestVerification(verificationRequest) + OutgoingVerificationViewEvent.StartSasVerification -> StateMachineEvent.StartSasVerification + OutgoingVerificationViewEvent.ConfirmVerification -> StateMachineEvent.AcceptChallenge + OutgoingVerificationViewEvent.DeclineVerification -> StateMachineEvent.DeclineChallenge + OutgoingVerificationViewEvent.Cancel -> StateMachineEvent.Cancel + OutgoingVerificationViewEvent.Reset -> StateMachineEvent.Reset }.let { stateMachineEvent -> stateAndDispatch.dispatchAction(stateMachineEvent) } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationState.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationState.kt index 6ba8a03a7f..5f1d7aa921 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationState.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationState.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.matrix.api.verification.VerificationRequest data class OutgoingVerificationState( val step: Step, val request: VerificationRequest.Outgoing, - val eventSink: (OutgoingVerificationViewEvents) -> Unit, + val eventSink: (OutgoingVerificationViewEvent) -> Unit, ) { @Stable sealed interface Step { diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStatePreviewParam.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStatePreviewParam.kt index ee3d5dd7a3..50abe14e9f 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStatePreviewParam.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStatePreviewParam.kt @@ -79,7 +79,7 @@ internal fun anOutgoingSessionVerificationRequest() = VerificationRequest.Outgoi internal fun anOutgoingVerificationState( step: Step = Step.Initial, request: VerificationRequest.Outgoing = anOutgoingSessionVerificationRequest(), - eventSink: (OutgoingVerificationViewEvents) -> Unit = {}, + eventSink: (OutgoingVerificationViewEvent) -> Unit = {}, ) = OutgoingVerificationState( step = step, request = request, diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationView.kt index 978ced6587..c9dc4ef45c 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationView.kt @@ -65,13 +65,13 @@ fun OutgoingVerificationView( val step = state.step fun cancelOrResetFlow() { when (step) { - is Step.Canceled -> state.eventSink(OutgoingVerificationViewEvents.Reset) + is Step.Canceled -> state.eventSink(OutgoingVerificationViewEvent.Reset) Step.Initial -> onBack() Step.Completed -> onFinish() - Step.Ready, is Step.AwaitingOtherDeviceResponse -> state.eventSink(OutgoingVerificationViewEvents.Cancel) + Step.Ready, is Step.AwaitingOtherDeviceResponse -> state.eventSink(OutgoingVerificationViewEvent.Cancel) is Step.Verifying -> { if (!step.state.isLoading()) { - state.eventSink(OutgoingVerificationViewEvents.DeclineVerification) + state.eventSink(OutgoingVerificationViewEvent.DeclineVerification) } } else -> Unit @@ -261,7 +261,7 @@ private fun OutgoingVerificationBottomMenu( text = stringResource(CommonStrings.action_start_verification), enabled = !isWaiting, showProgress = isWaiting, - onClick = { eventSink(OutgoingVerificationViewEvents.RequestVerification) }, + onClick = { eventSink(OutgoingVerificationViewEvent.RequestVerification) }, ) InvisibleButton() } @@ -281,7 +281,7 @@ private fun OutgoingVerificationBottomMenu( Button( modifier = Modifier.fillMaxWidth(), text = stringResource(CommonStrings.action_start), - onClick = { eventSink(OutgoingVerificationViewEvents.StartSasVerification) }, + onClick = { eventSink(OutgoingVerificationViewEvent.StartSasVerification) }, ) TextButton( modifier = Modifier.fillMaxWidth(), @@ -299,7 +299,7 @@ private fun OutgoingVerificationBottomMenu( enabled = !isVerifying, showProgress = isVerifying, onClick = { - eventSink(OutgoingVerificationViewEvents.ConfirmVerification) + eventSink(OutgoingVerificationViewEvent.ConfirmVerification) }, ) TextButton( @@ -307,7 +307,7 @@ private fun OutgoingVerificationBottomMenu( text = stringResource(R.string.screen_session_verification_they_dont_match), enabled = !isVerifying, onClick = { - eventSink(OutgoingVerificationViewEvents.DeclineVerification) + eventSink(OutgoingVerificationViewEvent.DeclineVerification) }, ) } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationViewEvents.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationViewEvent.kt similarity index 75% rename from features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationViewEvents.kt rename to features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationViewEvent.kt index 9faab7e790..2edfa21b0c 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationViewEvents.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationViewEvent.kt @@ -8,11 +8,11 @@ package io.element.android.features.verifysession.impl.outgoing -sealed interface OutgoingVerificationViewEvents { - data object RequestVerification : OutgoingVerificationViewEvents - data object StartSasVerification : OutgoingVerificationViewEvents - data object ConfirmVerification : OutgoingVerificationViewEvents - data object DeclineVerification : OutgoingVerificationViewEvents - data object Cancel : OutgoingVerificationViewEvents - data object Reset : OutgoingVerificationViewEvents +sealed interface OutgoingVerificationViewEvent { + data object RequestVerification : OutgoingVerificationViewEvent + data object StartSasVerification : OutgoingVerificationViewEvent + data object ConfirmVerification : OutgoingVerificationViewEvent + data object DeclineVerification : OutgoingVerificationViewEvent + data object Cancel : OutgoingVerificationViewEvent + data object Reset : OutgoingVerificationViewEvent } diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt index 577683d12e..e2cb533159 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt @@ -102,7 +102,7 @@ class OutgoingVerificationPresenterTest { val initialState = awaitItem() assertThat(initialState.step).isEqualTo(Step.Initial) val eventSink = initialState.eventSink - eventSink(OutgoingVerificationViewEvents.Cancel) + eventSink(OutgoingVerificationViewEvent.Cancel) assertThat(awaitItem().step).isEqualTo(Step.Exit) } @@ -118,7 +118,7 @@ class OutgoingVerificationPresenterTest { val presenter = createOutgoingVerificationPresenter(service) presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) - state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification) + state.eventSink(OutgoingVerificationViewEvent.ConfirmVerification) // Cancelling assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java) service.emitVerificationFlowState(VerificationFlowState.DidFail) @@ -134,7 +134,7 @@ class OutgoingVerificationPresenterTest { ) val presenter = createOutgoingVerificationPresenter(service) presenter.test { - awaitItem().eventSink(OutgoingVerificationViewEvents.RequestVerification) + awaitItem().eventSink(OutgoingVerificationViewEvent.RequestVerification) service.emitVerificationFlowState(VerificationFlowState.DidFail) assertThat(awaitItem().step).isInstanceOf(Step.AwaitingOtherDeviceResponse::class.java) assertThat(awaitItem().step).isEqualTo(Step.Canceled) @@ -151,7 +151,7 @@ class OutgoingVerificationPresenterTest { val presenter = createOutgoingVerificationPresenter(service) presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) - state.eventSink(OutgoingVerificationViewEvents.Cancel) + state.eventSink(OutgoingVerificationViewEvent.Cancel) assertThat(awaitItem().step).isEqualTo(Step.Canceled) } } @@ -181,7 +181,7 @@ class OutgoingVerificationPresenterTest { val state = requestVerificationAndAwaitVerifyingState(service) service.emitVerificationFlowState(VerificationFlowState.DidCancel) assertThat(awaitItem().step).isEqualTo(Step.Canceled) - state.eventSink(OutgoingVerificationViewEvents.Reset) + state.eventSink(OutgoingVerificationViewEvent.Reset) // Went back to initial state assertThat(awaitItem().step).isEqualTo(Step.Initial) cancelAndIgnoreRemainingEvents() @@ -204,7 +204,7 @@ class OutgoingVerificationPresenterTest { service, SessionVerificationData.Emojis(emojis) ) - state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification) + state.eventSink(OutgoingVerificationViewEvent.ConfirmVerification) assertThat(awaitItem().step).isEqualTo( Step.Verifying( SessionVerificationData.Emojis(emojis), @@ -235,7 +235,7 @@ class OutgoingVerificationPresenterTest { ) presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) - state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification) + state.eventSink(OutgoingVerificationViewEvent.ConfirmVerification) assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java) service.emitVerificationFlowState(VerificationFlowState.DidFinish) assertThat(awaitItem().step).isEqualTo(Step.Completed) @@ -261,7 +261,7 @@ class OutgoingVerificationPresenterTest { ) presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) - state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification) + state.eventSink(OutgoingVerificationViewEvent.ConfirmVerification) assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java) service.emitVerificationFlowState(VerificationFlowState.DidFinish) assertThat(awaitItem().step).isEqualTo(Step.Completed) @@ -286,7 +286,7 @@ class OutgoingVerificationPresenterTest { ) presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) - state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification) + state.eventSink(OutgoingVerificationViewEvent.ConfirmVerification) assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java) service.emitVerificationFlowState(VerificationFlowState.DidFinish) // The recovery state never leaves UNKNOWN, so the 10 seconds timeout fires and the flow completes anyway @@ -306,7 +306,7 @@ class OutgoingVerificationPresenterTest { val presenter = createOutgoingVerificationPresenter(service) presenter.test { val state = requestVerificationAndAwaitVerifyingState(service) - state.eventSink(OutgoingVerificationViewEvents.DeclineVerification) + state.eventSink(OutgoingVerificationViewEvent.DeclineVerification) assertThat(awaitItem().step).isEqualTo( Step.Verifying( SessionVerificationData.Emojis(emptyList()), @@ -362,7 +362,7 @@ class OutgoingVerificationPresenterTest { ): OutgoingVerificationState { var state = awaitItem() assertThat(state.step).isEqualTo(Step.Initial) - state.eventSink(OutgoingVerificationViewEvents.RequestVerification) + state.eventSink(OutgoingVerificationViewEvent.RequestVerification) testScope.advanceUntilIdle() // Await for other device response: fakeService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest) @@ -371,7 +371,7 @@ class OutgoingVerificationPresenterTest { // Await for the state to be Ready state = awaitItem() assertThat(state.step).isEqualTo(Step.Ready) - state.eventSink(OutgoingVerificationViewEvents.StartSasVerification) + state.eventSink(OutgoingVerificationViewEvent.StartSasVerification) testScope.advanceUntilIdle() // Await for other device response (again): fakeService.emitVerificationFlowState(VerificationFlowState.DidStartSasVerification) diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationViewTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationViewTest.kt index 2b5ec63ced..a186c56f3f 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationViewTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationViewTest.kt @@ -29,7 +29,7 @@ import org.junit.Test class OutgoingVerificationViewTest : RobolectricTest() { @Test fun `back key pressed - when canceled resets the flow`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setOutgoingVerificationView( anOutgoingVerificationState( step = OutgoingVerificationState.Step.Canceled, @@ -37,12 +37,12 @@ class OutgoingVerificationViewTest : RobolectricTest() { ), ) pressBackKey() - eventsRecorder.assertSingle(OutgoingVerificationViewEvents.Reset) + eventsRecorder.assertSingle(OutgoingVerificationViewEvent.Reset) } @Test fun `back key pressed - when awaiting response cancels the verification`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setOutgoingVerificationView( anOutgoingVerificationState( step = OutgoingVerificationState.Step.AwaitingOtherDeviceResponse, @@ -50,12 +50,12 @@ class OutgoingVerificationViewTest : RobolectricTest() { ), ) pressBackKey() - eventsRecorder.assertSingle(OutgoingVerificationViewEvents.Cancel) + eventsRecorder.assertSingle(OutgoingVerificationViewEvent.Cancel) } @Test fun `back key pressed - when ready to verify cancels the verification`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setOutgoingVerificationView( anOutgoingVerificationState( step = OutgoingVerificationState.Step.Ready, @@ -63,12 +63,12 @@ class OutgoingVerificationViewTest : RobolectricTest() { ), ) pressBackKey() - eventsRecorder.assertSingle(OutgoingVerificationViewEvents.Cancel) + eventsRecorder.assertSingle(OutgoingVerificationViewEvent.Cancel) } @Test fun `back key pressed - when verifying and not loading declines the verification`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setOutgoingVerificationView( anOutgoingVerificationState( step = OutgoingVerificationState.Step.Verifying( @@ -79,12 +79,12 @@ class OutgoingVerificationViewTest : RobolectricTest() { ), ) pressBackKey() - eventsRecorder.assertSingle(OutgoingVerificationViewEvents.DeclineVerification) + eventsRecorder.assertSingle(OutgoingVerificationViewEvent.DeclineVerification) } @Test fun `back key pressed - when verifying and loading does nothing`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setOutgoingVerificationView( anOutgoingVerificationState( step = OutgoingVerificationState.Step.Verifying( @@ -113,7 +113,7 @@ class OutgoingVerificationViewTest : RobolectricTest() { @Test fun `when flow is completed and the user clicks on the done button, the expected callback is invoked`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> setOutgoingVerificationView( anOutgoingVerificationState( @@ -128,7 +128,7 @@ class OutgoingVerificationViewTest : RobolectricTest() { @Test fun `clicking on they match emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setOutgoingVerificationView( anOutgoingVerificationState( step = OutgoingVerificationState.Step.Verifying( @@ -139,12 +139,12 @@ class OutgoingVerificationViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_session_verification_they_match) - eventsRecorder.assertSingle(OutgoingVerificationViewEvents.ConfirmVerification) + eventsRecorder.assertSingle(OutgoingVerificationViewEvent.ConfirmVerification) } @Test fun `clicking on they do not match emits the expected event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setOutgoingVerificationView( anOutgoingVerificationState( step = OutgoingVerificationState.Step.Verifying( @@ -155,7 +155,7 @@ class OutgoingVerificationViewTest : RobolectricTest() { ), ) clickOn(R.string.screen_session_verification_they_dont_match) - eventsRecorder.assertSingle(OutgoingVerificationViewEvents.DeclineVerification) + eventsRecorder.assertSingle(OutgoingVerificationViewEvent.DeclineVerification) } private fun AndroidComposeUiTest.setOutgoingVerificationView( From 2b55ff097f50b339fda658e98c95a8e9acd73301 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:31:09 +0200 Subject: [PATCH 068/112] `ViewFileEvents` -> `ViewFileEvent` --- .../impl/file/{ViewFileEvents.kt => ViewFileEvent.kt} | 6 +++--- .../features/viewfolder/impl/file/ViewFilePresenter.kt | 6 +++--- .../android/features/viewfolder/impl/file/ViewFileState.kt | 2 +- .../android/features/viewfolder/impl/file/ViewFileView.kt | 4 ++-- .../features/viewfolder/test/file/ViewFilePresenterTest.kt | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) rename features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/{ViewFileEvents.kt => ViewFileEvent.kt} (72%) diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileEvents.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileEvent.kt similarity index 72% rename from features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileEvents.kt rename to features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileEvent.kt index 2cc4f3a601..2ff6faf4ef 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileEvents.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileEvent.kt @@ -8,7 +8,7 @@ package io.element.android.features.viewfolder.impl.file -sealed interface ViewFileEvents { - data object SaveOnDisk : ViewFileEvents - data object Share : ViewFileEvents +sealed interface ViewFileEvent { + data object SaveOnDisk : ViewFileEvent + data object Share : ViewFileEvent } diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFilePresenter.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFilePresenter.kt index dd294bd53c..571f3eb053 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFilePresenter.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFilePresenter.kt @@ -44,10 +44,10 @@ class ViewFilePresenter( val coroutineScope = rememberCoroutineScope() val colorationMode = remember { name.toColorationMode() } - fun handleEvent(event: ViewFileEvents) { + fun handleEvent(event: ViewFileEvent) { when (event) { - ViewFileEvents.Share -> coroutineScope.share(path) - ViewFileEvents.SaveOnDisk -> coroutineScope.save(path) + ViewFileEvent.Share -> coroutineScope.share(path) + ViewFileEvent.SaveOnDisk -> coroutineScope.save(path) } } diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileState.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileState.kt index 979e188b65..e4f9255a14 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileState.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileState.kt @@ -14,7 +14,7 @@ data class ViewFileState( val name: String, val lines: AsyncData>, val colorationMode: ColorationMode, - val eventSink: (ViewFileEvents) -> Unit, + val eventSink: (ViewFileEvent) -> Unit, ) enum class ColorationMode { diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileView.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileView.kt index 8b44f12ba8..3c4ce9674c 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileView.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileView.kt @@ -48,7 +48,7 @@ fun ViewFileView( actions = { IconButton( onClick = { - state.eventSink(ViewFileEvents.Share) + state.eventSink(ViewFileEvent.Share) }, ) { Icon( @@ -58,7 +58,7 @@ fun ViewFileView( } IconButton( onClick = { - state.eventSink(ViewFileEvents.SaveOnDisk) + state.eventSink(ViewFileEvent.SaveOnDisk) }, ) { Icon( diff --git a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/ViewFilePresenterTest.kt b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/ViewFilePresenterTest.kt index 1a031ade28..c3be8d84b9 100644 --- a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/ViewFilePresenterTest.kt +++ b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/ViewFilePresenterTest.kt @@ -16,7 +16,7 @@ import io.element.android.features.viewfolder.impl.file.ColorationMode import io.element.android.features.viewfolder.impl.file.FileContentReader import io.element.android.features.viewfolder.impl.file.FileSave import io.element.android.features.viewfolder.impl.file.FileShare -import io.element.android.features.viewfolder.impl.file.ViewFileEvents +import io.element.android.features.viewfolder.impl.file.ViewFileEvent import io.element.android.features.viewfolder.impl.file.ViewFilePresenter import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.test.AN_EXCEPTION @@ -86,7 +86,7 @@ class ViewFilePresenterTest { }.test { skipItems(1) val initialState = awaitItem() - initialState.eventSink(ViewFileEvents.Share) + initialState.eventSink(ViewFileEvent.Share) assertThat(fileShare.hasBeenCalled).isTrue() assertThat(fileSave.hasBeenCalled).isFalse() } @@ -120,7 +120,7 @@ class ViewFilePresenterTest { }.test { skipItems(1) val initialState = awaitItem() - initialState.eventSink(ViewFileEvents.SaveOnDisk) + initialState.eventSink(ViewFileEvent.SaveOnDisk) assertThat(fileShare.hasBeenCalled).isFalse() assertThat(fileSave.hasBeenCalled).isTrue() } From cb8c462bad8f797fa6cc3cce20c5ce37607fd4eb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:31:51 +0200 Subject: [PATCH 069/112] `FullScreenIntentPermissionsEvents` -> `FullScreenIntentPermissionsEvent` --- .../impl/components/FullScreenIntentPermissionBanner.kt | 6 +++--- .../impl/notifications/NotificationSettingsView.kt | 4 ++-- ...sionsEvents.kt => FullScreenIntentPermissionsEvent.kt} | 6 +++--- .../api/FullScreenIntentPermissionsState.kt | 2 +- .../api/FullScreenIntentPermissionsStateProvider.kt | 2 +- .../impl/FullScreenIntentPermissionsPresenter.kt | 8 ++++---- .../test/FullScreenIntentPermissionsPresenterTest.kt | 8 ++++---- 7 files changed, 18 insertions(+), 18 deletions(-) rename libraries/fullscreenintent/api/src/main/kotlin/io/element/android/libraries/fullscreenintent/api/{FullScreenIntentPermissionsEvents.kt => FullScreenIntentPermissionsEvent.kt} (62%) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/FullScreenIntentPermissionBanner.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/FullScreenIntentPermissionBanner.kt index 92a5d8dbaa..f2826415d2 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/FullScreenIntentPermissionBanner.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/FullScreenIntentPermissionBanner.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.designsystem.components.Announcement import io.element.android.libraries.designsystem.components.AnnouncementType import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsEvents +import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsEvent import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState import io.element.android.libraries.fullscreenintent.api.aFullScreenIntentPermissionsState import io.element.android.libraries.ui.strings.CommonStrings @@ -31,8 +31,8 @@ fun FullScreenIntentPermissionBanner( description = stringResource(R.string.full_screen_intent_banner_message), type = AnnouncementType.Actionable( actionText = stringResource(CommonStrings.action_continue), - onDismissClick = { state.eventSink(FullScreenIntentPermissionsEvents.Dismiss) }, - onActionClick = { state.eventSink(FullScreenIntentPermissionsEvents.OpenSettings) }, + onDismissClick = { state.eventSink(FullScreenIntentPermissionsEvent.Dismiss) }, + onActionClick = { state.eventSink(FullScreenIntentPermissionsEvent.OpenSettings) }, ), modifier = modifier.roomListBannerPadding(), ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt index 6c5b053b35..21f0cc7b51 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt @@ -54,7 +54,7 @@ import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.OnLifecycleEvent -import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsEvents +import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsEvent import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.preferences.api.store.NotificationSound import io.element.android.libraries.ui.strings.CommonStrings @@ -165,7 +165,7 @@ private fun NotificationSettingsContentView( Text(stringResource(R.string.full_screen_intent_banner_message)) }, onClick = { - state.fullScreenIntentPermissionsState.eventSink(FullScreenIntentPermissionsEvents.OpenSettings) + state.fullScreenIntentPermissionsState.eventSink(FullScreenIntentPermissionsEvent.OpenSettings) } ) } diff --git a/libraries/fullscreenintent/api/src/main/kotlin/io/element/android/libraries/fullscreenintent/api/FullScreenIntentPermissionsEvents.kt b/libraries/fullscreenintent/api/src/main/kotlin/io/element/android/libraries/fullscreenintent/api/FullScreenIntentPermissionsEvent.kt similarity index 62% rename from libraries/fullscreenintent/api/src/main/kotlin/io/element/android/libraries/fullscreenintent/api/FullScreenIntentPermissionsEvents.kt rename to libraries/fullscreenintent/api/src/main/kotlin/io/element/android/libraries/fullscreenintent/api/FullScreenIntentPermissionsEvent.kt index 1c5bc4b6a0..fc39eb96a9 100644 --- a/libraries/fullscreenintent/api/src/main/kotlin/io/element/android/libraries/fullscreenintent/api/FullScreenIntentPermissionsEvents.kt +++ b/libraries/fullscreenintent/api/src/main/kotlin/io/element/android/libraries/fullscreenintent/api/FullScreenIntentPermissionsEvent.kt @@ -8,7 +8,7 @@ package io.element.android.libraries.fullscreenintent.api -sealed interface FullScreenIntentPermissionsEvents { - data object Dismiss : FullScreenIntentPermissionsEvents - data object OpenSettings : FullScreenIntentPermissionsEvents +sealed interface FullScreenIntentPermissionsEvent { + data object Dismiss : FullScreenIntentPermissionsEvent + data object OpenSettings : FullScreenIntentPermissionsEvent } diff --git a/libraries/fullscreenintent/api/src/main/kotlin/io/element/android/libraries/fullscreenintent/api/FullScreenIntentPermissionsState.kt b/libraries/fullscreenintent/api/src/main/kotlin/io/element/android/libraries/fullscreenintent/api/FullScreenIntentPermissionsState.kt index 5b1ddb91ed..238e11bd6c 100644 --- a/libraries/fullscreenintent/api/src/main/kotlin/io/element/android/libraries/fullscreenintent/api/FullScreenIntentPermissionsState.kt +++ b/libraries/fullscreenintent/api/src/main/kotlin/io/element/android/libraries/fullscreenintent/api/FullScreenIntentPermissionsState.kt @@ -11,5 +11,5 @@ package io.element.android.libraries.fullscreenintent.api data class FullScreenIntentPermissionsState( val permissionGranted: Boolean, val shouldDisplayBanner: Boolean, - val eventSink: (FullScreenIntentPermissionsEvents) -> Unit, + val eventSink: (FullScreenIntentPermissionsEvent) -> Unit, ) diff --git a/libraries/fullscreenintent/api/src/main/kotlin/io/element/android/libraries/fullscreenintent/api/FullScreenIntentPermissionsStateProvider.kt b/libraries/fullscreenintent/api/src/main/kotlin/io/element/android/libraries/fullscreenintent/api/FullScreenIntentPermissionsStateProvider.kt index 7248a24414..2e42908851 100644 --- a/libraries/fullscreenintent/api/src/main/kotlin/io/element/android/libraries/fullscreenintent/api/FullScreenIntentPermissionsStateProvider.kt +++ b/libraries/fullscreenintent/api/src/main/kotlin/io/element/android/libraries/fullscreenintent/api/FullScreenIntentPermissionsStateProvider.kt @@ -11,7 +11,7 @@ package io.element.android.libraries.fullscreenintent.api fun aFullScreenIntentPermissionsState( permissionGranted: Boolean = true, shouldDisplay: Boolean = false, - eventSink: (FullScreenIntentPermissionsEvents) -> Unit = {}, + eventSink: (FullScreenIntentPermissionsEvent) -> Unit = {}, ) = FullScreenIntentPermissionsState( permissionGranted = permissionGranted, shouldDisplayBanner = shouldDisplay, diff --git a/libraries/fullscreenintent/impl/src/main/kotlin/io/element/android/libraries/fullscreenintent/impl/FullScreenIntentPermissionsPresenter.kt b/libraries/fullscreenintent/impl/src/main/kotlin/io/element/android/libraries/fullscreenintent/impl/FullScreenIntentPermissionsPresenter.kt index d4a3e75d1a..7d726ccdcc 100644 --- a/libraries/fullscreenintent/impl/src/main/kotlin/io/element/android/libraries/fullscreenintent/impl/FullScreenIntentPermissionsPresenter.kt +++ b/libraries/fullscreenintent/impl/src/main/kotlin/io/element/android/libraries/fullscreenintent/impl/FullScreenIntentPermissionsPresenter.kt @@ -25,7 +25,7 @@ import dev.zacsweers.metro.Inject import dev.zacsweers.metro.SingleIn import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.meta.BuildMeta -import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsEvents +import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsEvent import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory import io.element.android.services.toolbox.api.intent.ExternalIntentLauncher @@ -64,12 +64,12 @@ class FullScreenIntentPermissionsPresenter( val isGranted = notificationManagerCompat.canUseFullScreenIntent() val isBannerDismissed by isFullScreenIntentBannerDismissed.collectAsState(initial = true) - fun handleEvent(event: FullScreenIntentPermissionsEvents) { + fun handleEvent(event: FullScreenIntentPermissionsEvent) { when (event) { - FullScreenIntentPermissionsEvents.Dismiss -> coroutineScope.launch { + FullScreenIntentPermissionsEvent.Dismiss -> coroutineScope.launch { dismissFullScreenIntentBanner() } - FullScreenIntentPermissionsEvents.OpenSettings -> openFullScreenIntentSettings() + FullScreenIntentPermissionsEvent.OpenSettings -> openFullScreenIntentSettings() } } diff --git a/libraries/fullscreenintent/impl/src/test/kotlin/io/element/android/libraries/fullscreenintent/test/FullScreenIntentPermissionsPresenterTest.kt b/libraries/fullscreenintent/impl/src/test/kotlin/io/element/android/libraries/fullscreenintent/test/FullScreenIntentPermissionsPresenterTest.kt index 44a5579e86..08bc2ed885 100644 --- a/libraries/fullscreenintent/impl/src/test/kotlin/io/element/android/libraries/fullscreenintent/test/FullScreenIntentPermissionsPresenterTest.kt +++ b/libraries/fullscreenintent/impl/src/test/kotlin/io/element/android/libraries/fullscreenintent/test/FullScreenIntentPermissionsPresenterTest.kt @@ -16,7 +16,7 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.core.meta.BuildMeta -import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsEvents +import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsEvent import io.element.android.libraries.fullscreenintent.impl.FullScreenIntentPermissionsPresenter import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.preferences.test.FakePreferenceDataStoreFactory @@ -78,7 +78,7 @@ class FullScreenIntentPermissionsPresenterTest { }.test { skipItems(1) val loadedItem = awaitItem() - loadedItem.eventSink(FullScreenIntentPermissionsEvents.Dismiss) + loadedItem.eventSink(FullScreenIntentPermissionsEvent.Dismiss) runCurrent() assertThat(awaitItem().shouldDisplayBanner).isFalse() } @@ -94,7 +94,7 @@ class FullScreenIntentPermissionsPresenterTest { }.test { skipItems(1) val loadedItem = awaitItem() - loadedItem.eventSink(FullScreenIntentPermissionsEvents.OpenSettings) + loadedItem.eventSink(FullScreenIntentPermissionsEvent.OpenSettings) launchLambda.assertions().isCalledOnce() cancelAndIgnoreRemainingEvents() } @@ -113,7 +113,7 @@ class FullScreenIntentPermissionsPresenterTest { }.test { skipItems(1) val loadedItem = awaitItem() - loadedItem.eventSink(FullScreenIntentPermissionsEvents.OpenSettings) + loadedItem.eventSink(FullScreenIntentPermissionsEvent.OpenSettings) launchLambda.assertions().isNeverCalled() cancelAndIgnoreRemainingEvents() } From 217ccb75ed2a34cb7df7967d5b5f24b162468df7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:32:34 +0200 Subject: [PATCH 070/112] `BatteryOptimizationEvents` -> `BatteryOptimizationEvent` --- .../home/impl/components/BatteryOptimizationBanner.kt | 6 +++--- ...yOptimizationEvents.kt => BatteryOptimizationEvent.kt} | 6 +++--- .../push/api/battery/BatteryOptimizationState.kt | 2 +- .../push/api/battery/BatteryOptimizationStateProvider.kt | 2 +- .../push/impl/battery/BatteryOptimizationPresenter.kt | 8 ++++---- .../push/impl/battery/BatteryOptimizationPresenterTest.kt | 8 ++++---- 6 files changed, 16 insertions(+), 16 deletions(-) rename libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/battery/{BatteryOptimizationEvents.kt => BatteryOptimizationEvent.kt} (77%) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/BatteryOptimizationBanner.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/BatteryOptimizationBanner.kt index c6a9480bc9..95826665fe 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/BatteryOptimizationBanner.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/BatteryOptimizationBanner.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.designsystem.components.Announcement import io.element.android.libraries.designsystem.components.AnnouncementType import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.push.api.battery.BatteryOptimizationEvents +import io.element.android.libraries.push.api.battery.BatteryOptimizationEvent import io.element.android.libraries.push.api.battery.BatteryOptimizationState import io.element.android.libraries.push.api.battery.aBatteryOptimizationState @@ -31,8 +31,8 @@ internal fun BatteryOptimizationBanner( description = stringResource(R.string.banner_battery_optimization_content_android), type = AnnouncementType.Actionable( actionText = stringResource(R.string.banner_battery_optimization_submit_android), - onActionClick = { state.eventSink(BatteryOptimizationEvents.RequestDisableOptimizations) }, - onDismissClick = { state.eventSink(BatteryOptimizationEvents.Dismiss) }, + onActionClick = { state.eventSink(BatteryOptimizationEvent.RequestDisableOptimizations) }, + onDismissClick = { state.eventSink(BatteryOptimizationEvent.Dismiss) }, ), ) } diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/battery/BatteryOptimizationEvents.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/battery/BatteryOptimizationEvent.kt similarity index 77% rename from libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/battery/BatteryOptimizationEvents.kt rename to libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/battery/BatteryOptimizationEvent.kt index b7a01cc61f..56e6a9caa7 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/battery/BatteryOptimizationEvents.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/battery/BatteryOptimizationEvent.kt @@ -8,7 +8,7 @@ package io.element.android.libraries.push.api.battery -sealed interface BatteryOptimizationEvents { - data object Dismiss : BatteryOptimizationEvents - data object RequestDisableOptimizations : BatteryOptimizationEvents +sealed interface BatteryOptimizationEvent { + data object Dismiss : BatteryOptimizationEvent + data object RequestDisableOptimizations : BatteryOptimizationEvent } diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/battery/BatteryOptimizationState.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/battery/BatteryOptimizationState.kt index 6732f7c17d..6a938eaec1 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/battery/BatteryOptimizationState.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/battery/BatteryOptimizationState.kt @@ -10,5 +10,5 @@ package io.element.android.libraries.push.api.battery data class BatteryOptimizationState( val shouldDisplayBanner: Boolean, - val eventSink: (BatteryOptimizationEvents) -> Unit, + val eventSink: (BatteryOptimizationEvent) -> Unit, ) diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/battery/BatteryOptimizationStateProvider.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/battery/BatteryOptimizationStateProvider.kt index 684185f40a..66531fb13b 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/battery/BatteryOptimizationStateProvider.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/battery/BatteryOptimizationStateProvider.kt @@ -10,7 +10,7 @@ package io.element.android.libraries.push.api.battery fun aBatteryOptimizationState( shouldDisplayBanner: Boolean = false, - eventSink: (BatteryOptimizationEvents) -> Unit = {}, + eventSink: (BatteryOptimizationEvent) -> Unit = {}, ) = BatteryOptimizationState( shouldDisplayBanner = shouldDisplayBanner, eventSink = eventSink, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/battery/BatteryOptimizationPresenter.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/battery/BatteryOptimizationPresenter.kt index e9af494686..d2f421775c 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/battery/BatteryOptimizationPresenter.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/battery/BatteryOptimizationPresenter.kt @@ -18,7 +18,7 @@ import androidx.compose.runtime.setValue import androidx.lifecycle.compose.LifecycleResumeEffect import dev.zacsweers.metro.Inject import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.push.api.battery.BatteryOptimizationEvents +import io.element.android.libraries.push.api.battery.BatteryOptimizationEvent import io.element.android.libraries.push.api.battery.BatteryOptimizationState import io.element.android.libraries.push.impl.push.MutableBatteryOptimizationStore import io.element.android.libraries.push.impl.store.PushDataStore @@ -48,12 +48,12 @@ class BatteryOptimizationPresenter( onPauseOrDispose {} } - fun handleEvent(event: BatteryOptimizationEvents) { + fun handleEvent(event: BatteryOptimizationEvent) { when (event) { - BatteryOptimizationEvents.Dismiss -> coroutineScope.launch { + BatteryOptimizationEvent.Dismiss -> coroutineScope.launch { mutableBatteryOptimizationStore.onOptimizationBannerDismissed() } - BatteryOptimizationEvents.RequestDisableOptimizations -> { + BatteryOptimizationEvent.RequestDisableOptimizations -> { isRequestSent = true if (batteryOptimization.requestDisablingBatteryOptimization().not()) { // If not able to perform the request, ensure that we do not display the banner again diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/battery/BatteryOptimizationPresenterTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/battery/BatteryOptimizationPresenterTest.kt index d3b2c8da2b..65bc3cf155 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/battery/BatteryOptimizationPresenterTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/battery/BatteryOptimizationPresenterTest.kt @@ -10,7 +10,7 @@ package io.element.android.libraries.push.impl.battery import androidx.lifecycle.Lifecycle import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.push.api.battery.BatteryOptimizationEvents +import io.element.android.libraries.push.api.battery.BatteryOptimizationEvent import io.element.android.libraries.push.impl.push.FakeMutableBatteryOptimizationStore import io.element.android.libraries.push.impl.push.MutableBatteryOptimizationStore import io.element.android.libraries.push.impl.store.InMemoryPushDataStore @@ -98,7 +98,7 @@ class BatteryOptimizationPresenterTest { assertThat(initialState.shouldDisplayBanner).isFalse() val displayedItem = awaitItem() assertThat(displayedItem.shouldDisplayBanner).isTrue() - displayedItem.eventSink(BatteryOptimizationEvents.Dismiss) + displayedItem.eventSink(BatteryOptimizationEvent.Dismiss) onOptimizationBannerDismissedResult.assertions().isCalledOnce() } } @@ -124,7 +124,7 @@ class BatteryOptimizationPresenterTest { assertThat(initialState.shouldDisplayBanner).isFalse() val displayedItem = awaitItem() assertThat(displayedItem.shouldDisplayBanner).isTrue() - displayedItem.eventSink(BatteryOptimizationEvents.RequestDisableOptimizations) + displayedItem.eventSink(BatteryOptimizationEvent.RequestDisableOptimizations) requestDisablingBatteryOptimizationResult.assertions().isCalledOnce() onOptimizationBannerDismissedResult.assertions().isCalledOnce() } @@ -150,7 +150,7 @@ class BatteryOptimizationPresenterTest { assertThat(initialState.shouldDisplayBanner).isFalse() val displayedItem = awaitItem() assertThat(displayedItem.shouldDisplayBanner).isTrue() - displayedItem.eventSink(BatteryOptimizationEvents.RequestDisableOptimizations) + displayedItem.eventSink(BatteryOptimizationEvent.RequestDisableOptimizations) requestDisablingBatteryOptimizationResult.assertions().isCalledOnce() batteryOptimization.isIgnoringBatteryOptimizationsResult = true lifeCycleOwner.givenState(Lifecycle.State.RESUMED) From cfa63156e2d5f43c2caa2f0a4d18d2bf33a2e3a8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:33:16 +0200 Subject: [PATCH 071/112] `TroubleshootNotificationsEvents` -> `TroubleshootNotificationsEvent` --- ...ts.kt => TroubleshootNotificationsEvent.kt} | 8 ++++---- .../impl/TroubleshootNotificationsPresenter.kt | 8 ++++---- .../impl/TroubleshootNotificationsState.kt | 2 +- ...oubleshootNotificationsStatePreviewParam.kt | 2 +- .../impl/TroubleshootNotificationsView.kt | 6 +++--- .../TroubleshootNotificationsPresenterTest.kt | 6 +++--- .../impl/TroubleshootNotificationsViewTest.kt | 18 +++++++++--------- 7 files changed, 25 insertions(+), 25 deletions(-) rename libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/{TroubleshootNotificationsEvents.kt => TroubleshootNotificationsEvent.kt} (76%) diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsEvents.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsEvent.kt similarity index 76% rename from libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsEvents.kt rename to libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsEvent.kt index 94330907ea..a8ac210dd8 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsEvents.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsEvent.kt @@ -8,8 +8,8 @@ package io.element.android.libraries.troubleshoot.impl -sealed interface TroubleshootNotificationsEvents { - data object StartTests : TroubleshootNotificationsEvents - data object RetryFailedTests : TroubleshootNotificationsEvents - data class QuickFix(val testIndex: Int) : TroubleshootNotificationsEvents +sealed interface TroubleshootNotificationsEvent { + data object StartTests : TroubleshootNotificationsEvent + data object RetryFailedTests : TroubleshootNotificationsEvent + data class QuickFix(val testIndex: Int) : TroubleshootNotificationsEvent } diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenter.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenter.kt index 00958b1f99..a2c3cc9a58 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenter.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenter.kt @@ -38,19 +38,19 @@ class TroubleshootNotificationsPresenter( } val testSuiteState by troubleshootTestSuite.state.collectAsState() - fun handleEvent(event: TroubleshootNotificationsEvents) { + fun handleEvent(event: TroubleshootNotificationsEvent) { when (event) { - TroubleshootNotificationsEvents.StartTests -> coroutineScope.launch { + TroubleshootNotificationsEvent.StartTests -> coroutineScope.launch { troubleshootTestSuite.runTestSuite(this) } - is TroubleshootNotificationsEvents.QuickFix -> coroutineScope.launch { + is TroubleshootNotificationsEvent.QuickFix -> coroutineScope.launch { troubleshootTestSuite.quickFix( testIndex = event.testIndex, coroutineScope = this, navigator = navigator, ) } - TroubleshootNotificationsEvents.RetryFailedTests -> coroutineScope.launch { + TroubleshootNotificationsEvent.RetryFailedTests -> coroutineScope.launch { troubleshootTestSuite.retryFailedTest(this) } } diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsState.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsState.kt index 92f4d668c4..b6ce940a9f 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsState.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsState.kt @@ -10,7 +10,7 @@ package io.element.android.libraries.troubleshoot.impl data class TroubleshootNotificationsState( val testSuiteState: TroubleshootTestSuiteState, - val eventSink: (TroubleshootNotificationsEvents) -> Unit, + val eventSink: (TroubleshootNotificationsEvent) -> Unit, ) { val hasFailedTests: Boolean = testSuiteState.mainState.isFailure() } diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsStatePreviewParam.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsStatePreviewParam.kt index 54dd40617f..1f1aef6767 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsStatePreviewParam.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsStatePreviewParam.kt @@ -78,7 +78,7 @@ open class TroubleshootNotificationsStatePreviewParam : PreviewParameterProvider fun aTroubleshootNotificationsState( tests: List = emptyList(), - eventSink: (TroubleshootNotificationsEvents) -> Unit = {}, + eventSink: (TroubleshootNotificationsEvent) -> Unit = {}, ) = TroubleshootNotificationsState( eventSink = eventSink, testSuiteState = TroubleshootTestSuiteState( diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsView.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsView.kt index d168cfd764..f6b085bc14 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsView.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsView.kt @@ -44,7 +44,7 @@ fun TroubleshootNotificationsView( when (event) { Lifecycle.Event.ON_RESUME -> { if (state.hasFailedTests) { - state.eventSink(TroubleshootNotificationsEvents.RetryFailedTests) + state.eventSink(TroubleshootNotificationsEvent.RetryFailedTests) } } else -> Unit @@ -129,7 +129,7 @@ private fun ColumnScope.TroubleshootNotificationsContent(state: TroubleshootNoti TestSuiteView( testSuiteState = state.testSuiteState, onQuickFixClick = { - state.eventSink(TroubleshootNotificationsEvents.QuickFix(it)) + state.eventSink(TroubleshootNotificationsEvent.QuickFix(it)) } ) } @@ -181,7 +181,7 @@ private fun RunTestButton(state: TroubleshootNotificationsState) { } ), onClick = { - state.eventSink(TroubleshootNotificationsEvents.StartTests) + state.eventSink(TroubleshootNotificationsEvent.StartTests) }, modifier = Modifier.fillMaxWidth(), ) diff --git a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenterTest.kt b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenterTest.kt index c8f8382ecf..32ab07883b 100644 --- a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenterTest.kt +++ b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenterTest.kt @@ -42,7 +42,7 @@ class TroubleshootNotificationsPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(TroubleshootNotificationsEvents.StartTests) + initialState.eventSink(TroubleshootNotificationsEvent.StartTests) skipItems(1) val stateAfterStart = awaitItem() assertThat(stateAfterStart.testSuiteState.mainState).isEqualTo(AsyncAction.Loading) @@ -63,7 +63,7 @@ class TroubleshootNotificationsPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(TroubleshootNotificationsEvents.RetryFailedTests) + initialState.eventSink(TroubleshootNotificationsEvent.RetryFailedTests) skipItems(1) val stateAfterStart = awaitItem() assertThat(stateAfterStart.testSuiteState.mainState).isEqualTo(AsyncAction.Loading) @@ -160,7 +160,7 @@ class TroubleshootNotificationsPresenterTest { skipItems(1) val initialState = awaitItem() assertThat(initialState.testSuiteState.mainState).isInstanceOf(AsyncAction.Failure::class.java) - initialState.eventSink(TroubleshootNotificationsEvents.QuickFix(0)) + initialState.eventSink(TroubleshootNotificationsEvent.QuickFix(0)) val stateAfterStart = awaitItem() assertThat(stateAfterStart.testSuiteState.mainState).isEqualTo(AsyncAction.Loading) } diff --git a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsViewTest.kt b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsViewTest.kt index ea98aeabc6..b685eb2238 100644 --- a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsViewTest.kt +++ b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsViewTest.kt @@ -27,7 +27,7 @@ import org.robolectric.annotation.Config class TroubleshootNotificationsViewTest : RobolectricTest() { @Test fun `press menu back invokes the expected callback`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { setTroubleshootNotificationsView( state = aTroubleshootNotificationsState( @@ -41,20 +41,20 @@ class TroubleshootNotificationsViewTest : RobolectricTest() { @Test fun `clicking on run test emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setTroubleshootNotificationsView( aTroubleshootNotificationsState( eventSink = eventsRecorder ), ) onNodeWithText("Run tests").performClick() - eventsRecorder.assertSingle(TroubleshootNotificationsEvents.StartTests) + eventsRecorder.assertSingle(TroubleshootNotificationsEvent.StartTests) } @Config(qualifiers = "h1024dp") @Test fun `clicking on run test again emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setTroubleshootNotificationsView( aTroubleshootNotificationsState( tests = listOf( @@ -68,8 +68,8 @@ class TroubleshootNotificationsViewTest : RobolectricTest() { onNodeWithText("Run tests again").performClick() eventsRecorder.assertList( listOf( - TroubleshootNotificationsEvents.RetryFailedTests, - TroubleshootNotificationsEvents.StartTests, + TroubleshootNotificationsEvent.RetryFailedTests, + TroubleshootNotificationsEvent.StartTests, ) ) } @@ -77,7 +77,7 @@ class TroubleshootNotificationsViewTest : RobolectricTest() { @Config(qualifiers = "h1024dp") @Test fun `clicking on quick fix emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setTroubleshootNotificationsView( aTroubleshootNotificationsState( tests = listOf( @@ -91,8 +91,8 @@ class TroubleshootNotificationsViewTest : RobolectricTest() { onNodeWithText("Attempt to fix").performClick() eventsRecorder.assertList( listOf( - TroubleshootNotificationsEvents.RetryFailedTests, - TroubleshootNotificationsEvents.QuickFix(0), + TroubleshootNotificationsEvent.RetryFailedTests, + TroubleshootNotificationsEvent.QuickFix(0), ) ) } From 34a086261fbf0c86a658fae24a5cf1170529d464 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:33:58 +0200 Subject: [PATCH 072/112] `PushHistoryEvents` -> `PushHistoryEvent` --- ...shHistoryEvents.kt => PushHistoryEvent.kt} | 10 ++++---- .../impl/history/PushHistoryPresenter.kt | 10 ++++---- .../impl/history/PushHistoryState.kt | 2 +- .../history/PushHistoryStatePreviewParam.kt | 2 +- .../impl/history/PushHistoryView.kt | 12 +++++----- .../impl/history/PushHistoryPresenterTest.kt | 18 +++++++------- .../impl/history/PushHistoryViewTest.kt | 24 +++++++++---------- 7 files changed, 39 insertions(+), 39 deletions(-) rename libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/{PushHistoryEvents.kt => PushHistoryEvent.kt} (80%) diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryEvents.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryEvent.kt similarity index 80% rename from libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryEvents.kt rename to libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryEvent.kt index 5116e0e101..a73c466275 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryEvents.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryEvent.kt @@ -12,9 +12,9 @@ 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 -sealed interface PushHistoryEvents { - data class SetShowOnlyErrors(val showOnlyErrors: Boolean) : PushHistoryEvents - data class Reset(val requiresConfirmation: Boolean) : PushHistoryEvents - data class NavigateTo(val sessionId: SessionId, val roomId: RoomId, val eventId: EventId) : PushHistoryEvents - data object ClearDialog : PushHistoryEvents +sealed interface PushHistoryEvent { + data class SetShowOnlyErrors(val showOnlyErrors: Boolean) : PushHistoryEvent + data class Reset(val requiresConfirmation: Boolean) : PushHistoryEvent + data class NavigateTo(val sessionId: SessionId, val roomId: RoomId, val eventId: EventId) : PushHistoryEvent + data object ClearDialog : PushHistoryEvent } diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryPresenter.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryPresenter.kt index ad1878051d..94e220a726 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryPresenter.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryPresenter.kt @@ -62,12 +62,12 @@ class PushHistoryPresenter( var resetAction: AsyncAction by remember { mutableStateOf(AsyncAction.Uninitialized) } var showNotSameAccountError by remember { mutableStateOf(false) } - fun handleEvent(event: PushHistoryEvents) { + fun handleEvent(event: PushHistoryEvent) { when (event) { - is PushHistoryEvents.SetShowOnlyErrors -> { + is PushHistoryEvent.SetShowOnlyErrors -> { showOnlyErrors = event.showOnlyErrors } - is PushHistoryEvents.Reset -> { + is PushHistoryEvent.Reset -> { if (event.requiresConfirmation) { resetAction = AsyncAction.ConfirmingNoParams } else { @@ -78,11 +78,11 @@ class PushHistoryPresenter( } } } - PushHistoryEvents.ClearDialog -> { + PushHistoryEvent.ClearDialog -> { resetAction = AsyncAction.Uninitialized showNotSameAccountError = false } - is PushHistoryEvents.NavigateTo -> { + is PushHistoryEvent.NavigateTo -> { if (event.sessionId != sessionId) { showNotSameAccountError = true } else { diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryState.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryState.kt index dbc98bdbe3..7afa567aa1 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryState.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryState.kt @@ -18,5 +18,5 @@ data class PushHistoryState( val showOnlyErrors: Boolean, val resetAction: AsyncAction, val showNotSameAccountError: Boolean, - val eventSink: (PushHistoryEvents) -> Unit, + val eventSink: (PushHistoryEvent) -> Unit, ) diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryStatePreviewParam.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryStatePreviewParam.kt index 968de5534c..cec0069237 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryStatePreviewParam.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryStatePreviewParam.kt @@ -53,7 +53,7 @@ fun aPushHistoryState( showOnlyErrors: Boolean = false, resetAction: AsyncAction = AsyncAction.Uninitialized, showNotSameAccountError: Boolean = false, - eventSink: (PushHistoryEvents) -> Unit = {}, + eventSink: (PushHistoryEvent) -> Unit = {}, ) = PushHistoryState( pushCounter = pushCounter, pushHistoryItems = pushHistoryItems.toImmutableList(), diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryView.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryView.kt index 7315cb4e5a..be10a2845e 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryView.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryView.kt @@ -100,14 +100,14 @@ fun PushHistoryView( }, onClick = { showMenu = false - state.eventSink(PushHistoryEvents.SetShowOnlyErrors(state.showOnlyErrors.not())) + state.eventSink(PushHistoryEvent.SetShowOnlyErrors(state.showOnlyErrors.not())) }, ) DropdownMenuItem( text = { Text(stringResource(id = CommonStrings.action_reset)) }, onClick = { showMenu = false - state.eventSink(PushHistoryEvents.Reset(requiresConfirmation = true)) + state.eventSink(PushHistoryEvent.Reset(requiresConfirmation = true)) }, ) } @@ -132,8 +132,8 @@ fun PushHistoryView( title = stringResource(CommonStrings.dialog_title_confirmation), submitText = stringResource(CommonStrings.action_reset), cancelText = stringResource(CommonStrings.action_cancel), - onSubmitClick = { state.eventSink(PushHistoryEvents.Reset(requiresConfirmation = false)) }, - onDismiss = { state.eventSink(PushHistoryEvents.ClearDialog) }, + onSubmitClick = { state.eventSink(PushHistoryEvent.Reset(requiresConfirmation = false)) }, + onDismiss = { state.eventSink(PushHistoryEvent.ClearDialog) }, ) }, onErrorDismiss = {}, @@ -142,7 +142,7 @@ fun PushHistoryView( if (state.showNotSameAccountError) { ErrorDialog( content = "Please switch account first to navigate to the event.", - onSubmit = { state.eventSink(PushHistoryEvents.ClearDialog) } + onSubmit = { state.eventSink(PushHistoryEvent.ClearDialog) } ) } } @@ -175,7 +175,7 @@ private fun PushHistoryContent( val roomId = pushHistory.roomId val eventId = pushHistory.eventId if (sessionId != null && roomId != null && eventId != null) { - state.eventSink(PushHistoryEvents.NavigateTo(sessionId, roomId, eventId)) + state.eventSink(PushHistoryEvent.NavigateTo(sessionId, roomId, eventId)) } } ) diff --git a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryPresenterTest.kt b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryPresenterTest.kt index 15fb6b5f25..e378cc9af6 100644 --- a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryPresenterTest.kt +++ b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryPresenterTest.kt @@ -73,9 +73,9 @@ class PushHistoryPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(PushHistoryEvents.Reset(requiresConfirmation = true)) + initialState.eventSink(PushHistoryEvent.Reset(requiresConfirmation = true)) assertThat(awaitItem().resetAction).isEqualTo(AsyncAction.ConfirmingNoParams) - initialState.eventSink(PushHistoryEvents.ClearDialog) + initialState.eventSink(PushHistoryEvent.ClearDialog) assertThat(awaitItem().resetAction).isEqualTo(AsyncAction.Uninitialized) resetPushHistoryResult.assertions().isNeverCalled() } @@ -92,9 +92,9 @@ class PushHistoryPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(PushHistoryEvents.Reset(requiresConfirmation = true)) + initialState.eventSink(PushHistoryEvent.Reset(requiresConfirmation = true)) assertThat(awaitItem().resetAction).isEqualTo(AsyncAction.ConfirmingNoParams) - initialState.eventSink(PushHistoryEvents.Reset(requiresConfirmation = false)) + initialState.eventSink(PushHistoryEvent.Reset(requiresConfirmation = false)) assertThat(awaitItem().resetAction).isEqualTo(AsyncAction.Loading) assertThat(awaitItem().resetAction).isEqualTo(AsyncAction.Uninitialized) resetPushHistoryResult.assertions().isCalledOnce() @@ -115,13 +115,13 @@ class PushHistoryPresenterTest { pushService.emitPushHistoryItems(listOf(item, itemError)) awaitItem().let { state -> assertThat(state.pushHistoryItems).containsExactly(item, itemError) - state.eventSink(PushHistoryEvents.SetShowOnlyErrors(showOnlyErrors = true)) + state.eventSink(PushHistoryEvent.SetShowOnlyErrors(showOnlyErrors = true)) } skipItems(1) awaitItem().let { state -> assertThat(state.showOnlyErrors).isTrue() assertThat(state.pushHistoryItems).containsExactly(itemError) - state.eventSink(PushHistoryEvents.SetShowOnlyErrors(showOnlyErrors = false)) + state.eventSink(PushHistoryEvent.SetShowOnlyErrors(showOnlyErrors = false)) } skipItems(1) awaitItem().let { state -> @@ -142,7 +142,7 @@ class PushHistoryPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink( - PushHistoryEvents.NavigateTo( + PushHistoryEvent.NavigateTo( sessionId = A_SESSION_ID, roomId = A_ROOM_ID, eventId = AN_EVENT_ID, @@ -160,7 +160,7 @@ class PushHistoryPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink( - PushHistoryEvents.NavigateTo( + PushHistoryEvent.NavigateTo( sessionId = A_SESSION_ID_2, roomId = A_ROOM_ID, eventId = AN_EVENT_ID, @@ -168,7 +168,7 @@ class PushHistoryPresenterTest { ) assertThat(awaitItem().showNotSameAccountError).isTrue() // Reset error - initialState.eventSink(PushHistoryEvents.ClearDialog) + initialState.eventSink(PushHistoryEvent.ClearDialog) assertThat(awaitItem().showNotSameAccountError).isFalse() } } diff --git a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryViewTest.kt b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryViewTest.kt index b9f615d68e..989f7bdc10 100644 --- a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryViewTest.kt +++ b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryViewTest.kt @@ -30,8 +30,8 @@ import org.junit.Test class PushHistoryViewTest : RobolectricTest() { @Test - fun `clicking on Reset sends a PushHistoryEvents`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + fun `clicking on Reset sends a PushHistoryEvent`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() setPushHistoryView( aPushHistoryState( pushCounter = 123, @@ -41,14 +41,14 @@ class PushHistoryViewTest : RobolectricTest() { val menuContentDescription = activity!!.getString(CommonStrings.a11y_user_menu) onNodeWithContentDescription(menuContentDescription).performClick() clickOn(CommonStrings.action_reset) - eventsRecorder.assertSingle(PushHistoryEvents.Reset(requiresConfirmation = true)) + eventsRecorder.assertSingle(PushHistoryEvent.Reset(requiresConfirmation = true)) // Also check that the push counter is rendered onNodeWithText("123").assertExists() } @Test - fun `clicking on show only errors sends a PushHistoryEvents(true)`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + fun `clicking on show only errors sends a PushHistoryEvent(true)`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() setPushHistoryView( aPushHistoryState( showOnlyErrors = false, @@ -58,12 +58,12 @@ class PushHistoryViewTest : RobolectricTest() { val menuContentDescription = activity!!.getString(CommonStrings.a11y_user_menu) onNodeWithContentDescription(menuContentDescription).performClick() onNodeWithText("Show only errors").performClick() - eventsRecorder.assertSingle(PushHistoryEvents.SetShowOnlyErrors(showOnlyErrors = true)) + eventsRecorder.assertSingle(PushHistoryEvent.SetShowOnlyErrors(showOnlyErrors = true)) } @Test - fun `clicking on show only errors sends a PushHistoryEvents(false)`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + fun `clicking on show only errors sends a PushHistoryEvent(false)`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() setPushHistoryView( aPushHistoryState( showOnlyErrors = true, @@ -73,12 +73,12 @@ class PushHistoryViewTest : RobolectricTest() { val menuContentDescription = activity!!.getString(CommonStrings.a11y_user_menu) onNodeWithContentDescription(menuContentDescription).performClick() onNodeWithText("Show only errors").performClick() - eventsRecorder.assertSingle(PushHistoryEvents.SetShowOnlyErrors(showOnlyErrors = false)) + eventsRecorder.assertSingle(PushHistoryEvent.SetShowOnlyErrors(showOnlyErrors = false)) } @Test fun `clicking on an invalid event has no effect`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder(expectEvents = false) + val eventsRecorder = EventsRecorder(expectEvents = false) setPushHistoryView( aPushHistoryState( pushHistoryItems = listOf( @@ -95,7 +95,7 @@ class PushHistoryViewTest : RobolectricTest() { @Test fun `clicking on a valid event emits the expected Event`() = runAndroidComposeUiTest { - val eventsRecorder = EventsRecorder() + val eventsRecorder = EventsRecorder() setPushHistoryView( aPushHistoryState( pushHistoryItems = listOf( @@ -111,7 +111,7 @@ class PushHistoryViewTest : RobolectricTest() { ) onNodeWithText(A_FORMATTED_DATE).performClick() eventsRecorder.assertSingle( - PushHistoryEvents.NavigateTo( + PushHistoryEvent.NavigateTo( sessionId = A_SESSION_ID, roomId = A_ROOM_ID, eventId = AN_EVENT_ID, From c62ff733fbad8a54ea530980c34ad9ab7f1d64cc Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 25 Aug 2026 08:37:53 +0000 Subject: [PATCH 073/112] Update screenshots --- .../features.home.impl.spaces_HomeSpacesView_Day_2_en.png | 4 ++-- .../features.home.impl.spaces_HomeSpacesView_Night_2_en.png | 4 ++-- .../images/features.home.impl_HomeView_Day_16_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_16_en.png | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_2_en.png index 03d7d97186..be9847ac64 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ecd79d3f8c4efbb54efa6a5b8dee79274ae9957b01146025dab42a45fc328fc0 -size 24740 +oid sha256:e3f09104202b0d1ba857ef376c32733bb598bebe1dbb47fd2765f7bbf5f756eb +size 22218 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_2_en.png index 049aa93a8a..3a37cae365 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0fd37517b8913e2bac3275f717aa15bed18055a7525e0404b25b43cb8bd8422d -size 23840 +oid sha256:037171836a783617d4c6907e73e4738c38bdef6c328302a14f8dcb5535105e41 +size 21553 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_16_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_16_en.png index 559a928a97..17ff86bf54 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_16_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_16_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a694440a4ac9db68a24ef77f0502b3936370974b1f6932c49accb6736eb5566 -size 39875 +oid sha256:2e2d1b5923dd2941bd7e60a9019a7770ed3383529ad9a8f3572c9e3d450310e0 +size 37461 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_16_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_16_en.png index cbe5cf415b..a26f43f289 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_16_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_16_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c748d1ae791d77ddfed18024a4ea04188adba1ac00aa43b5faa7660153cc9209 -size 34734 +oid sha256:858ed6993ba8d322e9acc40ca3e759372e19cd3aae9854709f374c0c8d1c4429 +size 32549 From 71689595075cb0461634d83588444a32f618660d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:54:01 +0200 Subject: [PATCH 074/112] Fix documentation: `Events` -> `Event` --- docs/_developer_onboarding.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_developer_onboarding.md b/docs/_developer_onboarding.md index e7515eb466..01ad915126 100644 --- a/docs/_developer_onboarding.md +++ b/docs/_developer_onboarding.md @@ -322,7 +322,7 @@ Example for a new feature called RoomDetails: 4. Verify that the structure looks ok and click on Finish 5. The modules api/impl should be created under `features/roomdetails` directory. 6. Sync project with Gradle so the modules are recognized (no need to add them to settings.gradle). -7. You can now add more Presentation classes (Events, State, StatePreviewParam, View, Presenter) in the impl module with the `Template Presentation Classes`. +7. You can now add more Presentation classes (Event, State, StatePreviewParam, View, Presenter) in the impl module with the `Template Presentation Classes`. To use it, just right click on the package where you want to generate classes, and click on `New` / `Template Presentation Classes`. Fill the text field with the base name of the classes: the generated files are `Event.kt`, `State.kt`, `StatePreviewParam.kt`, `Presenter.kt`, `Node.kt` and `View.kt`. For instance From d7310b69415b13990468d3ea32b7524f91d86a73 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 10:55:38 +0200 Subject: [PATCH 075/112] Split code into several files. --- .../impl/threads/list/ThreadsListEvent.kt | 12 ++++++++++ .../impl/threads/list/ThreadsListPresenter.kt | 17 -------------- .../impl/threads/list/ThreadsListState.kt | 22 +++++++++++++++++++ 3 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListEvent.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListState.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListEvent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListEvent.kt new file mode 100644 index 0000000000..191d5d111b --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListEvent.kt @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.threads.list + +sealed interface ThreadsListEvent { + data object Paginate : ThreadsListEvent +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListPresenter.kt index ba1366df44..d6d928326c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListPresenter.kt @@ -20,13 +20,10 @@ import io.element.android.features.messages.impl.utils.messagesummary.MessageSum import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.dateformatter.api.DateFormatter import io.element.android.libraries.dateformatter.api.DateFormatterMode -import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.threads.ThreadListPaginationStatus import io.element.android.libraries.matrix.ui.model.getAvatarData -import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.onStart @@ -129,17 +126,3 @@ class ThreadsListPresenter( ) } } - -data class ThreadsListState( - val roomId: RoomId, - val roomName: String, - val roomAvatarUrl: String?, - val isRoomTombstoned: Boolean, - val heroes: ImmutableList, - val threads: ImmutableList, - val eventSink: (ThreadsListEvent) -> Unit, -) - -sealed interface ThreadsListEvent { - data object Paginate : ThreadsListEvent -} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListState.kt new file mode 100644 index 0000000000..8aafd49d21 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListState.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.threads.list + +import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.matrix.api.core.RoomId +import kotlinx.collections.immutable.ImmutableList + +data class ThreadsListState( + val roomId: RoomId, + val roomName: String, + val roomAvatarUrl: String?, + val isRoomTombstoned: Boolean, + val heroes: ImmutableList, + val threads: ImmutableList, + val eventSink: (ThreadsListEvent) -> Unit, +) From 0a9a25d940d2aea027806a419e016922dfe07eff Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 11:21:10 +0200 Subject: [PATCH 076/112] Update Konsist test and fix existing issue. --- .../android/features/home/impl/search/GlobalSearchEvent.kt | 3 --- .../android/tests/konsist/KonsistArchitectureTest.kt | 6 +++--- .../element/android/tests/konsist/KonsistImmutableTest.kt | 7 +++++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/GlobalSearchEvent.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/GlobalSearchEvent.kt index 76d9147e94..5f8093b1de 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/GlobalSearchEvent.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/GlobalSearchEvent.kt @@ -7,9 +7,6 @@ package io.element.android.features.home.impl.search -import androidx.compose.runtime.Immutable - -@Immutable sealed interface GlobalSearchEvent { object ClearQuery : GlobalSearchEvent object ToggleSearchVisibility : GlobalSearchEvent diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistArchitectureTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistArchitectureTest.kt index c0e62482fa..26f9ef50a3 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistArchitectureTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistArchitectureTest.kt @@ -49,12 +49,12 @@ class KonsistArchitectureTest { } @Test - fun `Events MUST be sealed interface`() { + fun `Event MUST be sealed interface`() { Konsist.scopeFromProject() .classes() .withSealedModifier() - .withNameEndingWith("Events") - .assertEmpty(additionalMessage = "Events class MUST be sealed interface") + .withNameEndingWith("Event") + .assertEmpty(additionalMessage = "Event class MUST be sealed interface") } @Test diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistImmutableTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistImmutableTest.kt index ceb0058124..8534c50b6e 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistImmutableTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistImmutableTest.kt @@ -67,11 +67,14 @@ class KonsistImmutableTest { } @Test - fun `Immutable annotation is not used on sealed interface for Presenter Events`() { + fun `Immutable annotation is not used on sealed interface for Presenter Event`() { Konsist .scopeFromProduction() .interfaces() - .withNameEndingWith("Events") + .withoutName( + "LatestEvent", + ) + .withNameEndingWith("Event") .withAnnotationOf(Immutable::class) .assertEmpty() } From 0634cadc9ccb971994527a19e17e74f6c054c4ce Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 11:36:23 +0200 Subject: [PATCH 077/112] Ensure translation of "Chats" does not render in big font when displayed on 2 lines. They can add the end be displayed on one line using the small font. --- .../home/impl/components/HomeTopBar.kt | 11 +- .../designsystem/text/AdaptativeTitle.kt | 144 ++++++++++++++++++ 2 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/AdaptativeTitle.kt diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt index 1a351be9b6..9f4a2403f7 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt @@ -53,8 +53,6 @@ import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.heading -import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.LayoutDirection @@ -80,6 +78,7 @@ import io.element.android.libraries.designsystem.modifiers.backgroundVerticalGra import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE +import io.element.android.libraries.designsystem.text.AdaptativeTitle import io.element.android.libraries.designsystem.theme.components.DropdownMenu import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem import io.element.android.libraries.designsystem.theme.components.Icon @@ -149,12 +148,10 @@ fun HomeTopBar( // "Chats" else -> ElementTheme.typography.fontHeadingLgBold } - Text( - modifier = Modifier.semantics { - heading() - }, + AdaptativeTitle( + title = displayTitle, style = style, - text = displayTitle, + twoLinesStyle = ElementTheme.typography.fontHeadingSmMedium, ) } }, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/AdaptativeTitle.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/AdaptativeTitle.kt new file mode 100644 index 0000000000..f73347d67f --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/AdaptativeTitle.kt @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.designsystem.text + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.heading +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.rememberTextMeasurer +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Text + +/** + * Render the title of a screen, but adapt the style if it does not fit on a single line, + * so that a wrapped title does not look oversized. + * + * @param title the title to render. + * @param style the style to use to render the [title]. + * @param twoLinesStyle the style to use instead of [style] when the [title] does not fit on a single line with [style], + * so that a wrapped title does not look oversized. + * @param modifier the [Modifier] to apply to this layout. + */ +@Composable +fun AdaptativeTitle( + title: String, + style: TextStyle, + twoLinesStyle: TextStyle, + modifier: Modifier = Modifier, +) { + // If style and twoLinesStyle are the same, we don't need to measure the text. + if (style == twoLinesStyle) { + Text( + modifier = modifier.semantics { + heading() + }, + style = style, + maxLines = 2, + text = title, + ) + } else { + val textMeasurer = rememberTextMeasurer() + BoxWithConstraints(modifier = modifier) { + // Measure the title before rendering it, so that the style is picked in a single layout pass. + val fitsOnASingleLine = textMeasurer.measure( + text = title, + style = style, + constraints = constraints, + ).lineCount == 1 + Text( + modifier = Modifier.semantics { + heading() + }, + style = if (fitsOnASingleLine) style else twoLinesStyle, + maxLines = 2, + text = title, + ) + } + } +} + +@PreviewsDayNight +@Composable +internal fun AdaptativeTitlePreview() = ElementPreview { + @Composable + fun AdaptativeTitleItem( + text: String, + content: @Composable () -> Unit, + ) { + Column( + modifier = Modifier + .width(180.dp) + .background(ElementTheme.colors.bgSubtlePrimary), + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + text = text, + style = ElementTheme.typography.fontBodyXsRegular, + color = ElementTheme.colors.textSecondary, + ) + content() + } + } + Column( + modifier = Modifier.padding(4.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + // Fits on a single line: the large style is kept. + AdaptativeTitleItem("Adaptative, 1 line:") { + AdaptativeTitle( + title = "Chats", + style = ElementTheme.typography.fontHeadingLgBold, + twoLinesStyle = ElementTheme.typography.fontHeadingSmMedium, + ) + } + // Would be rendered on two lines with the large style: the smaller style is used instead. + AdaptativeTitleItem("Adaptative, 2 lines:") { + AdaptativeTitle( + title = "Tutte le conversazioni:", + style = ElementTheme.typography.fontHeadingLgBold, + twoLinesStyle = ElementTheme.typography.fontHeadingSmMedium, + ) + } + // Would be rendered on two lines with the large style: the smaller style is used instead + // and it's finally rendered on a single line with the smaller style. + AdaptativeTitleItem("Adaptative, but 1 line:") { + AdaptativeTitle( + title = "Tous les chats", + style = ElementTheme.typography.fontHeadingLgBold, + twoLinesStyle = ElementTheme.typography.fontHeadingSmMedium, + ) + } + // Short title, with small font, 1 line. + AdaptativeTitleItem("Always small, 1 line:") { + AdaptativeTitle( + title = "My space", + style = ElementTheme.typography.fontHeadingSmMedium, + twoLinesStyle = ElementTheme.typography.fontHeadingSmMedium, + ) + } + // Space name, which can still be rendered on two lines. + AdaptativeTitleItem("Always small, 2 lines:") { + AdaptativeTitle( + title = "A very long space name", + style = ElementTheme.typography.fontHeadingSmMedium, + twoLinesStyle = ElementTheme.typography.fontHeadingSmMedium, + ) + } + } +} From 806f7c500c942f0c2f714da81f3187812c7865c5 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 25 Aug 2026 10:09:30 +0000 Subject: [PATCH 078/112] Update screenshots --- .../libraries.designsystem.text_AdaptativeTitle_Day_0_en.png | 3 +++ ...libraries.designsystem.text_AdaptativeTitle_Night_0_en.png | 3 +++ ...eme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png | 4 ++-- ...e.components_HorizontalFloatingToolbarNoFab_Night_0_en.png | 4 ++-- ...em.theme.components_HorizontalFloatingToolbar_Day_0_en.png | 4 ++-- ....theme.components_HorizontalFloatingToolbar_Night_0_en.png | 4 ++-- 6 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/libraries.designsystem.text_AdaptativeTitle_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.designsystem.text_AdaptativeTitle_Night_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.text_AdaptativeTitle_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.text_AdaptativeTitle_Day_0_en.png new file mode 100644 index 0000000000..87822df7c7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.text_AdaptativeTitle_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5bbefd78bcd53367f72ca11d4f57ea87903a677ff24ba4bc80cc50ddd33108fa +size 34859 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.text_AdaptativeTitle_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.text_AdaptativeTitle_Night_0_en.png new file mode 100644 index 0000000000..57b121b8fa --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.text_AdaptativeTitle_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:17941844b79274e31880c09b5ee8052693c4425eb7f09c3c2c8687bf2ef64863 +size 34113 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png index ee5c582240..58546b8522 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:75de341f705bb22955f9399180519b65ad59c2e1917317d56e50ea4db78f35bc -size 12102 +oid sha256:30357bf18792db5ab20085010251170a724ffe8c00313facd04ee704e806af64 +size 10982 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png index 52fd9b9693..fedead0fa8 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58b9491aaa5554b89d86c62a5c9c44975367538c6baa5562f8dc5768f76d8264 -size 8705 +oid sha256:4811667dec0f32accc5e2c8a5dd5c0b34017fcff8d1e0376ee7a4708426bca0f +size 9838 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en.png index 38ad0e4b56..83302d6ba4 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:326d59cf768b2dbd96eb7964d54c8eda509a5cd57c6e62087625e3314da0bb4d -size 13658 +oid sha256:16dee1dfe746cd40dac9fe8f0af3ecb42cd14bab5cc490917b589beed065ed1b +size 14686 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en.png index 687e0d436a..4da2a98aca 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d57df7110371c9224e232b707d6a1b10ce14afcb56c803a78220792f2a0d4cf2 -size 11354 +oid sha256:ef0dc4d53daeb986f378b6e4d85928b4b6729336c89cf21e642c88c53688a3ab +size 10292 From af7e7655997b3b6db8826101206337736e0898e9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 13:32:11 +0200 Subject: [PATCH 079/112] Fixing compilation issue after merging develop --- .../impl/outgoing/OutgoingVerificationPresenterTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt index a65437ba50..7dfacc40f1 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt @@ -152,7 +152,7 @@ class OutgoingVerificationPresenterTest { ) val presenter = createOutgoingVerificationPresenter(service) presenter.test { - awaitItem().eventSink(OutgoingVerificationViewEvents.RequestVerification) + awaitItem().eventSink(OutgoingVerificationViewEvent.RequestVerification) advanceTimeBy(1.minutes) assertThat(awaitItem().step).isEqualTo(Step.AwaitingOtherDeviceResponse) advanceTimeBy(9.minutes) From 23a60cf20a8140bff51541b705ad6860cfc1b1f8 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Tue, 25 Aug 2026 15:54:45 +0300 Subject: [PATCH 080/112] Point the proxy debugging doc at the method that builds the clients The doc told the reader to add disableSslVerification() to RustMatrixClientFactory.create(), which has not built the client for a long time; getBaseClientBuilder() is where the chain lives, and it covers both the login client and the restored session client. It also only described the emulator's own proxy setting, while the app already follows the device's global proxy through DefaultProxyProvider, which is the only way to do this on a physical device. --- docs/debug_proxying.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/debug_proxying.md b/docs/debug_proxying.md index a28163c959..f76c24eecb 100644 --- a/docs/debug_proxying.md +++ b/docs/debug_proxying.md @@ -9,13 +9,18 @@ 1) Set `127.0.0.1` as "Host name" and `8080` as "Port number". 1) Click "Apply" and verify that "Proxy status" is "Success" and close the settings window. Screenshot 2023-10-04 at 14 48 47 + 1) Alternatively, set the proxy on the device itself, which the app honours through `DefaultProxyProvider`, and which also works on a physical device: + ``` + adb shell settings put global http_proxy 10.0.2.2:8080 + ``` + Use `adb shell settings delete global http_proxy` to remove it afterwards. On a physical device, replace `10.0.2.2` with the address of the machine running mitmproxy. 1) Install the mitmproxy CA cert (this is needed to see traffic from java/kotlin code, it's not needed for traffic coming from native code e.g. the matrix-rust-sdk). 1) Open the emulator Chrome browser app 1) Go to the url `mitm.it` 1) Follow the instructions to install the CA cert on Android devices. Screenshot 2023-10-04 at 14 51 27 1) Slightly modify the Element X app source code. - 1) Go to the `RustMatrixClientFactory.create()` method. + 1) Go to the `RustMatrixClientFactory.getBaseClientBuilder()` method, which builds every client the app creates, whether it is logging in or restoring a session. 1) Add `.disableSslVerification()` in the `ClientBuilder` method chain. 1) Build and run the Element X app. 1) Enjoy, you will see all the traffic in mitmproxy's web interface. From 76b19a158f6c69962605173359754286a2c28381 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Tue, 25 Aug 2026 15:56:16 +0300 Subject: [PATCH 081/112] Show the build date in the screenshot gallery footer The gallery gave no indication of how old the screenshots on the page were, which is one of the remaining items on the GitHub Pages improvement list. The generator now writes the time it ran alongside the screenshot data, and the page renders it in the footer next to the existing links. --- screenshots/html/data.js | 1 + screenshots/html/screenshots.css | 5 +++++ screenshots/html/script.js | 10 +++++++++- tools/test/generateAllScreenshots.py | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/screenshots/html/data.js b/screenshots/html/data.js index 62cea77dcf..8cf60a31ef 100644 --- a/screenshots/html/data.js +++ b/screenshots/html/data.js @@ -1,4 +1,5 @@ // Generated file, do not edit +export const buildDate = 1787667600; export const screenshots = [ ["en","en-dark","de",], ["features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en","features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en",20675,], diff --git a/screenshots/html/screenshots.css b/screenshots/html/screenshots.css index f946c82e81..90842b7ac9 100644 --- a/screenshots/html/screenshots.css +++ b/screenshots/html/screenshots.css @@ -150,6 +150,11 @@ input { height: 40px; } +.build-date { + margin-right: 20px; + font-size: 13px; +} + a:link { color: white; font-size: 14px; diff --git a/screenshots/html/script.js b/screenshots/html/script.js index 4a9586d591..44b2522a5d 100644 --- a/screenshots/html/script.js +++ b/screenshots/html/script.js @@ -5,7 +5,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. */ -import { screenshots } from './data.js'; +import { buildDate, screenshots } from './data.js'; const URL_PARAM_LANGUAGES = "l"; const URL_PARAM_IMAGE_WIDTH = "w"; @@ -360,5 +360,13 @@ function addTable() { document.getElementById('lines').textContent = `${linesCounter} lines`; } +function addBuildDate() { + const element = document.createElement('div'); + element.className = 'build-date'; + element.textContent = `Built on ${new Date(buildDate * 1000).toLocaleDateString()}`; + document.getElementById('footer').appendChild(element); +} + addForm(); +addBuildDate(); addTable(); diff --git a/tools/test/generateAllScreenshots.py b/tools/test/generateAllScreenshots.py index e10a7abb74..a4e3468d80 100755 --- a/tools/test/generateAllScreenshots.py +++ b/tools/test/generateAllScreenshots.py @@ -167,6 +167,7 @@ def generateJavascriptFile(): with open("screenshots/html/data.js", "w") as f: f.write("// Generated file, do not edit\n") + f.write("export const buildDate = %d;\n" % int(time.time())) f.write("export const screenshots = [\n") for line in data: f.write("[") From 898baca9d5bfe44fd6a60d54ad2432ee6516fa2b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 15:25:50 +0200 Subject: [PATCH 082/112] SDK 26.08.25 --- 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 46890d253f..c22264eaf1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -185,7 +185,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version # https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt # All new features should not be implemented in the pull request that upgrades the version, developers should # only fix API breaks and may add some TODOs. -matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.08.19" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.08.25" # Others coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } From c893657aa01897b635f14510a9ee80f9f84ed103 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Tue, 25 Aug 2026 16:30:49 +0300 Subject: [PATCH 083/112] Document how the Element Call base URL developer option is used The developer option passes its value through to the widget URL verbatim, so it has to be the URL of the page that joins a room rather than the deployment root. That is not obvious from the field, and hughns asked for it to be written down in docs/ rather than expanded inline in the app, because the path depends on how the deployment was hosted. Part of #4475. --- docs/element_call.md | 53 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 docs/element_call.md diff --git a/docs/element_call.md b/docs/element_call.md new file mode 100644 index 0000000000..e4bd46e2cb --- /dev/null +++ b/docs/element_call.md @@ -0,0 +1,53 @@ +# Element Call + + + +* [Overview](#overview) +* [Setting a custom base URL](#setting-a-custom-base-url) +* [Why the application does not add the room path itself](#why-the-application-does-not-add-the-room-path-itself) +* [Where the value is read](#where-the-value-is-read) + + + +## Overview + +Element X embeds a copy of [Element Call](https://github.com/element-hq/element-call) in the +application assets and loads it from +`https://appassets.androidplatform.net/element-call/index.html`. Calls therefore work out of the box +with no extra deployment, and the embedded copy is the one every user gets. + +Developers can point the application at another Element Call deployment instead — a local +development server, a staging deployment, or a self-hosted one — with the **Element Call base URL** +developer option. + +## Setting a custom base URL + +The option lives in *Settings* → *Developer options* → *Element Call*, under "Element Call base +URL". On a release build the *Developer options* entry is hidden until the version number at the +bottom of *Settings* is tapped seven times. Leaving the field empty restores the embedded copy. + +The value is used verbatim as the widget base URL, so it must be the URL of the page that joins a +room, not the URL of the deployment root. For the public deployments that means: + +| Deployment | Value to enter | +|---|---| +| `call.element.io` | `https://call.element.io/room` | +| `call.element.dev` | `https://call.element.dev/room` | + +Entering `https://call.element.io` without the `/room` suffix loads the Element Call lobby rather +than the room, so the call is never joined. + +## Why the application does not add the room path itself + +The path that joins a room is a property of the deployment, not of Element Call. Unpacking the +Element Call release tarball behind a reverse proxy can put it at any path — `https://call.foo.tld`, +`https://call.foo.tld/some/path/somewhere` — and a proxy that already maps a subdomain onto +`https://1.2.3.4/room` would end up with the suffix twice. The application passes the value through +unchanged so that every one of those deployments can be reached. + +## Where the value is read + +`AppDeveloperSettingsView` renders the field and sends +`AppDeveloperSettingsEvent.SetCustomElementCallBaseUrl`, which `DefaultAppPreferencesStore` persists. +`DefaultCallWidgetProvider.getWidget` reads it back and falls back to the embedded copy when it is +unset. From 16bc825c173d8d9d8f847c951b32c49369457079 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 15:33:47 +0200 Subject: [PATCH 084/112] `username` has been renamed to `serverNameFromUserId`, but it is not necessary if we call `homeserverUrl`, so just remove it. --- .../android/libraries/matrix/impl/RustMatrixClientFactory.kt | 1 - .../matrix/impl/fixtures/fakes/FakeFfiClientBuilder.kt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt index 0d593626bb..b1d93fd903 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt @@ -99,7 +99,6 @@ class RustMatrixClientFactory( isMessageSearchAvailable = isMessageSearchAvailable, ) .homeserverUrl(sessionData.homeserverUrl) - .username(sessionData.userId) .let { (clientBuilderEnterpriseHook(RustMatrixClientBuilder(it), SessionId(sessionData.userId)) as RustMatrixClientBuilder).inner } .use { it.build() } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClientBuilder.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClientBuilder.kt index 623fe9a8cf..ba010ed728 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClientBuilder.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClientBuilder.kt @@ -42,7 +42,7 @@ class FakeFfiClientBuilder( override fun setSessionDelegate(sessionDelegate: ClientSessionDelegate) = this override fun slidingSyncVersionBuilder(versionBuilder: SlidingSyncVersionBuilder) = this override fun userAgent(userAgent: String) = this - override fun username(username: String) = this + override fun serverNameFromUserId(userId: String): ClientBuilder = this override fun enableShareHistoryOnInvite(enableShareHistoryOnInvite: Boolean): ClientBuilder = this override fun threadsEnabled(enabled: Boolean, threadSubscriptions: Boolean): ClientBuilder = this override fun sqliteStore(config: SqliteStoreBuilder): ClientBuilder = this From 776cfc9c808af57501f211739fe10352882a4883 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Aug 2026 15:55:51 +0200 Subject: [PATCH 085/112] Add new type RoomRetention --- .../element/android/libraries/matrix/api/room/StateEventType.kt | 1 + .../android/libraries/matrix/impl/room/StateEventType.kt | 2 ++ 2 files changed, 3 insertions(+) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/StateEventType.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/StateEventType.kt index 7bfc5dc6bb..2994683a61 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/StateEventType.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/StateEventType.kt @@ -35,6 +35,7 @@ sealed interface StateEventType { data object RoomImagePack : StateEventType data object RoomLanguage : StateEventType data object RoomPolicy : StateEventType + data object RoomRetention : StateEventType data class Custom(val type: String) : StateEventType } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/StateEventType.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/StateEventType.kt index 13a040525e..e64eb87570 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/StateEventType.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/StateEventType.kt @@ -39,6 +39,7 @@ fun StateEventType.map(): RustStateEventType = when (this) { StateEventType.RoomImagePack -> RustStateEventType.RoomImagePack StateEventType.RoomLanguage -> RustStateEventType.RoomLanguage StateEventType.RoomPolicy -> RustStateEventType.RoomPolicy + StateEventType.RoomRetention -> RustStateEventType.RoomRetention is StateEventType.Custom -> stateEventTypeFromString(type) } @@ -69,5 +70,6 @@ fun RustStateEventType.map(): StateEventType = when (this) { RustStateEventType.RoomImagePack -> StateEventType.RoomImagePack RustStateEventType.RoomLanguage -> StateEventType.RoomLanguage RustStateEventType.RoomPolicy -> StateEventType.RoomPolicy + RustStateEventType.RoomRetention -> StateEventType.RoomRetention is RustStateEventType.Custom -> StateEventType.Custom(this.toString()) } From 30c18b8a008208d9d252bf79a59326a9447e8a37 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Tue, 25 Aug 2026 20:51:27 +0300 Subject: [PATCH 086/112] Say when the server cannot be reached instead of showing nothing While the homeserver is unreachable but the device still has a working network, the app showed no indication at all: the syncing chip is hidden as soon as the SDK reports the session offline, and the offline banner only appears when the device itself has no connectivity. A blocked or down server was therefore indistinguishable from a quiet, healthy one. The chip now has three states rather than a boolean, and the third says "Server unreachable" for exactly that case. When the device has no network the chip stays hidden, because the offline banner already says so and two overlapping messages would be worse than one. Part of #4099 --- .../appnav/loggedin/LoggedInPresenter.kt | 15 ++++++++-- .../android/appnav/loggedin/LoggedInState.kt | 8 ++++- .../loggedin/LoggedInStatePreviewParam.kt | 7 +++-- .../android/appnav/loggedin/LoggedInView.kt | 2 +- .../android/appnav/loggedin/SyncStateView.kt | 28 +++++++++++++---- .../appnav/loggedin/LoggedInPresenterTest.kt | 30 ++++++++++++++++--- 6 files changed, 72 insertions(+), 18 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt index f6faee0acf..f2f6e1312f 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt @@ -105,9 +105,18 @@ class LoggedInPresenter( } val syncIndicator by matrixClient.roomListService.syncIndicator.collectAsState() val isOnline by syncService.isOnline.collectAsState() - val showSyncSpinner by remember { + val hasNetwork by networkMonitor.connectivity.collectAsState() + val syncIndicatorState by remember { derivedStateOf { - isOnline && syncIndicator == RoomListService.SyncIndicator.Show + when { + isOnline -> if (syncIndicator == RoomListService.SyncIndicator.Show) { + SyncIndicatorState.Syncing + } else { + SyncIndicatorState.Hidden + } + hasNetwork == NetworkStatus.Connected -> SyncIndicatorState.ServerUnreachable + else -> SyncIndicatorState.Hidden + } } } var forceNativeSlidingSyncMigration by remember { mutableStateOf(false) } @@ -181,7 +190,7 @@ class LoggedInPresenter( } return LoggedInState( - showSyncSpinner = showSyncSpinner, + syncIndicatorState = syncIndicatorState, pusherRegistrationState = pusherRegistrationState.value, ignoreRegistrationError = ignoreRegistrationError, forceNativeSlidingSyncMigration = forceNativeSlidingSyncMigration, diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt index 33499341e7..8ac675a146 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt @@ -11,8 +11,14 @@ package io.element.android.appnav.loggedin import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.permissions.api.localnetwork.LocalNetworkPermissionDialog +enum class SyncIndicatorState { + Hidden, + Syncing, + ServerUnreachable, +} + data class LoggedInState( - val showSyncSpinner: Boolean, + val syncIndicatorState: SyncIndicatorState, val pusherRegistrationState: AsyncData, val ignoreRegistrationError: Boolean, val forceNativeSlidingSyncMigration: Boolean, diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStatePreviewParam.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStatePreviewParam.kt index b479e00279..705f6fbe61 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStatePreviewParam.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStatePreviewParam.kt @@ -17,7 +17,8 @@ open class LoggedInStatePreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aLoggedInState(), - aLoggedInState(showSyncSpinner = true), + aLoggedInState(syncIndicatorState = SyncIndicatorState.Syncing), + aLoggedInState(syncIndicatorState = SyncIndicatorState.ServerUnreachable), aLoggedInState(pusherRegistrationState = AsyncData.Failure(PusherRegistrationFailure.NoDistributorsAvailable())), aLoggedInState(forceNativeSlidingSyncMigration = true), aLoggedInState(localNetworkPermissionDialog = LocalNetworkPermissionDialog.Rationale), @@ -26,13 +27,13 @@ open class LoggedInStatePreviewParam : PreviewParameterProvider { } fun aLoggedInState( - showSyncSpinner: Boolean = false, + syncIndicatorState: SyncIndicatorState = SyncIndicatorState.Hidden, pusherRegistrationState: AsyncData = AsyncData.Uninitialized, forceNativeSlidingSyncMigration: Boolean = false, appName: String = "Element X", localNetworkPermissionDialog: LocalNetworkPermissionDialog = LocalNetworkPermissionDialog.None, ) = LoggedInState( - showSyncSpinner = showSyncSpinner, + syncIndicatorState = syncIndicatorState, pusherRegistrationState = pusherRegistrationState, ignoreRegistrationError = false, forceNativeSlidingSyncMigration = forceNativeSlidingSyncMigration, diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt index 060a24855a..1d3a355663 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt @@ -47,7 +47,7 @@ fun LoggedInView( ) { SyncStateView( modifier = Modifier.align(Alignment.TopCenter), - isVisible = state.showSyncSpinner, + state = state.syncIndicatorState, ) } when (state.pusherRegistrationState) { diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt index 7c5f196a4c..db39658ad4 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt @@ -25,18 +25,23 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun SyncStateView( - isVisible: Boolean, + state: SyncIndicatorState, modifier: Modifier = Modifier ) { AnimatedVisibility( - visible = isVisible, + visible = state != SyncIndicatorState.Hidden, modifier = modifier, enter = fadeIn(spring(stiffness = 500F)), exit = fadeOut(spring(stiffness = 500F)), ) { - AsyncIndicator.Loading( - text = stringResource(id = CommonStrings.common_syncing), - ) + when (state) { + SyncIndicatorState.ServerUnreachable -> AsyncIndicator.Failure( + text = stringResource(id = CommonStrings.common_server_unreachable), + ) + else -> AsyncIndicator.Loading( + text = stringResource(id = CommonStrings.common_syncing), + ) + } } } @@ -46,7 +51,18 @@ internal fun SyncStateViewPreview() = ElementPreview { // Add a box to see the shadow Box(modifier = Modifier.padding(24.dp)) { SyncStateView( - isVisible = true + state = SyncIndicatorState.Syncing + ) + } +} + +@PreviewsDayNight +@Composable +internal fun SyncStateViewServerUnreachablePreview() = ElementPreview { + // Add a box to see the shadow + Box(modifier = Modifier.padding(24.dp)) { + SyncStateView( + state = SyncIndicatorState.ServerUnreachable ) } } 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 f85d35bbb5..474253c8df 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 @@ -67,7 +67,7 @@ class LoggedInPresenterTest { fun `present - initial state`() = runTest { createLoggedInPresenter().test { val initialState = awaitItem() - assertThat(initialState.showSyncSpinner).isFalse() + assertThat(initialState.syncIndicatorState).isEqualTo(SyncIndicatorState.Hidden) assertThat(initialState.pusherRegistrationState.isUninitialized()).isTrue() assertThat(initialState.ignoreRegistrationError).isFalse() } @@ -97,11 +97,33 @@ class LoggedInPresenterTest { matrixClient = FakeMatrixClient(roomListService = roomListService), ).test { val initialState = awaitItem() - assertThat(initialState.showSyncSpinner).isFalse() + assertThat(initialState.syncIndicatorState).isEqualTo(SyncIndicatorState.Hidden) roomListService.postSyncIndicator(RoomListService.SyncIndicator.Show) - consumeItemsUntilPredicate { it.showSyncSpinner } + consumeItemsUntilPredicate { it.syncIndicatorState == SyncIndicatorState.Syncing } roomListService.postSyncIndicator(RoomListService.SyncIndicator.Hide) - consumeItemsUntilPredicate { !it.showSyncSpinner } + consumeItemsUntilPredicate { it.syncIndicatorState != SyncIndicatorState.Syncing } + } + } + + @Test + fun `present - say the server is unreachable when the device has network but the sync is offline`() = runTest { + createLoggedInPresenter( + syncState = SyncState.Offline, + networkMonitor = FakeNetworkMonitor(initialStatus = NetworkStatus.Connected), + ).test { + consumeItemsUntilPredicate { it.syncIndicatorState == SyncIndicatorState.ServerUnreachable } + cancelAndConsumeRemainingEvents() + } + } + + @Test + fun `present - say nothing when the device itself has no network`() = runTest { + createLoggedInPresenter( + syncState = SyncState.Offline, + networkMonitor = FakeNetworkMonitor(initialStatus = NetworkStatus.Disconnected), + ).test { + consumeItemsUntilPredicate { it.syncIndicatorState == SyncIndicatorState.Hidden } + cancelAndConsumeRemainingEvents() } } From 65c2cd8a79855ce9be7f0c1a6b845fc10f1f7b93 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Tue, 25 Aug 2026 20:55:38 +0300 Subject: [PATCH 087/112] Drop the sender's name from the room list preview of two-person rooms The room list preview prefixes the sender's name unless the room is a direct message. A room that was created as a group and later converted to a direct message is not marked as one by the server, so its preview kept naming the only other person in it on every message, while iOS did not. The prefix now depends on how many people are actually in the room rather than on the direct message flag, which is the behaviour asked for in the issue: no name while only two people can be talking, the name back as soon as a third joins. Part of #1548 --- .../datasource/RoomListRoomSummaryFactory.kt | 7 ++++- .../RoomListRoomSummaryFactoryTest.kt | 26 +++++++++++++++++++ .../test/FakeRoomLatestEventFormatter.kt | 5 ++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/datasource/RoomListRoomSummaryFactory.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/datasource/RoomListRoomSummaryFactory.kt index 96e9f9344b..ba3e8592b6 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/datasource/RoomListRoomSummaryFactory.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/datasource/RoomListRoomSummaryFactory.kt @@ -19,6 +19,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.eventformatter.api.RoomLatestEventFormatter import io.element.android.libraries.matrix.api.room.CallIntentConsensus import io.element.android.libraries.matrix.api.room.CurrentUserMembership +import io.element.android.libraries.matrix.api.room.RoomInfo import io.element.android.libraries.matrix.api.roomlist.LatestEventValue import io.element.android.libraries.matrix.api.roomlist.RoomSummary import io.element.android.libraries.matrix.ui.model.dmUserStatus @@ -47,7 +48,7 @@ class RoomListRoomSummaryFactory( mode = DateFormatterMode.TimeOrDate, useRelative = true, ), - latestEvent = computeLatestEvent(roomSummary.latestEvent, roomInfo.isDm), + latestEvent = computeLatestEvent(roomSummary.latestEvent, roomInfo.hasOnlyTwoMembers()), avatarData = avatarData, userDefinedNotificationMode = roomInfo.userDefinedNotificationMode, hasRoomCall = roomInfo.hasRoomCall, @@ -81,6 +82,10 @@ class RoomListRoomSummaryFactory( ) } + private fun RoomInfo.hasOnlyTwoMembers(): Boolean { + return isDm || activeMembersCount <= 2 + } + private fun computeLatestEvent(latestEvent: LatestEventValue, dm: Boolean): LatestEvent { return when (latestEvent) { is LatestEventValue.None -> { diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/datasource/RoomListRoomSummaryFactoryTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/datasource/RoomListRoomSummaryFactoryTest.kt index 5f307918be..bfd92094a5 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/datasource/RoomListRoomSummaryFactoryTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/datasource/RoomListRoomSummaryFactoryTest.kt @@ -8,10 +8,36 @@ package io.element.android.features.home.impl.datasource +import com.google.common.truth.Truth.assertThat import io.element.android.libraries.dateformatter.api.DateFormatter import io.element.android.libraries.dateformatter.test.FakeDateFormatter import io.element.android.libraries.eventformatter.api.RoomLatestEventFormatter import io.element.android.libraries.eventformatter.test.FakeRoomLatestEventFormatter +import io.element.android.libraries.matrix.test.room.aRoomInfo +import io.element.android.libraries.matrix.test.room.aRoomSummary +import org.junit.Test + +class RoomListRoomSummaryFactoryTest { + @Test + fun `a room with two members does not prefix its preview with the sender`() { + val formatter = FakeRoomLatestEventFormatter() + val factory = aRoomListRoomSummaryFactory(roomLatestEventFormatter = formatter) + + factory.create(aRoomSummary(info = aRoomInfo(isDm = false, activeMembersCount = 2))) + + assertThat(formatter.lastIsDmRoom).isTrue() + } + + @Test + fun `a room with more than two members prefixes its preview with the sender`() { + val formatter = FakeRoomLatestEventFormatter() + val factory = aRoomListRoomSummaryFactory(roomLatestEventFormatter = formatter) + + factory.create(aRoomSummary(info = aRoomInfo(isDm = false, activeMembersCount = 3))) + + assertThat(formatter.lastIsDmRoom).isFalse() + } +} fun aRoomListRoomSummaryFactory( dateFormatter: DateFormatter = FakeDateFormatter { _, _, _ -> "Today" }, diff --git a/libraries/eventformatter/test/src/main/kotlin/io/element/android/libraries/eventformatter/test/FakeRoomLatestEventFormatter.kt b/libraries/eventformatter/test/src/main/kotlin/io/element/android/libraries/eventformatter/test/FakeRoomLatestEventFormatter.kt index 5e1a056a6c..de1b40e14c 100644 --- a/libraries/eventformatter/test/src/main/kotlin/io/element/android/libraries/eventformatter/test/FakeRoomLatestEventFormatter.kt +++ b/libraries/eventformatter/test/src/main/kotlin/io/element/android/libraries/eventformatter/test/FakeRoomLatestEventFormatter.kt @@ -14,11 +14,16 @@ import io.element.android.libraries.matrix.api.roomlist.LatestEventValue class FakeRoomLatestEventFormatter : RoomLatestEventFormatter { private var result: CharSequence? = null + var lastIsDmRoom: Boolean? = null + private set + override fun format(latestEvent: LatestEventValue.Local, isDmRoom: Boolean): CharSequence? { + lastIsDmRoom = isDmRoom return result } override fun format(latestEvent: LatestEventValue.Remote, isDmRoom: Boolean): CharSequence? { + lastIsDmRoom = isDmRoom return result } From 69ba9cf21e96b75de05e0bb4ace4d386771b0981 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 25 Aug 2026 18:10:19 +0000 Subject: [PATCH 088/112] Update screenshots --- .../images/appnav.loggedin_LoggedInView_Day_2_en.png | 4 ++-- .../images/appnav.loggedin_LoggedInView_Day_3_en.png | 4 ++-- .../images/appnav.loggedin_LoggedInView_Day_4_en.png | 4 ++-- .../images/appnav.loggedin_LoggedInView_Day_5_en.png | 4 ++-- .../images/appnav.loggedin_LoggedInView_Day_6_en.png | 3 +++ .../images/appnav.loggedin_LoggedInView_Night_2_en.png | 4 ++-- .../images/appnav.loggedin_LoggedInView_Night_3_en.png | 4 ++-- .../images/appnav.loggedin_LoggedInView_Night_4_en.png | 4 ++-- .../images/appnav.loggedin_LoggedInView_Night_5_en.png | 4 ++-- .../images/appnav.loggedin_LoggedInView_Night_6_en.png | 3 +++ ...ppnav.loggedin_SyncStateViewServerUnreachable_Day_0_en.png | 3 +++ ...nav.loggedin_SyncStateViewServerUnreachable_Night_0_en.png | 3 +++ 12 files changed, 28 insertions(+), 16 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_6_en.png create mode 100644 tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_6_en.png create mode 100644 tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateViewServerUnreachable_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateViewServerUnreachable_Night_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_2_en.png index 6082c07349..ae687af6c7 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:062413bedff67d6264f7c498db69e38babdfad4cc5b5c0a581f9031dcd03ae10 -size 36968 +oid sha256:fdce2c0a9c41b04870e79cc5e0413bf09ddea7907650d798cc4206ece881933f +size 9782 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_3_en.png index ad2925d791..6082c07349 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ed6c634f81fca7a0d8cf242f294d37fe44b4dd5f0832c1bb1e29df245f06546 -size 24161 +oid sha256:062413bedff67d6264f7c498db69e38babdfad4cc5b5c0a581f9031dcd03ae10 +size 36968 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_4_en.png index 5adfe35003..ad2925d791 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:965cd4692e88a413e5e0a83e16a190386e8f199716fdd7288f4e05b8c623a8b2 -size 33756 +oid sha256:8ed6c634f81fca7a0d8cf242f294d37fe44b4dd5f0832c1bb1e29df245f06546 +size 24161 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_5_en.png index bf9aed6830..5adfe35003 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:771c01d4563687aff208b0f279c9fb98192b0cd2b2ba56d43627f6c785e92b90 -size 32956 +oid sha256:965cd4692e88a413e5e0a83e16a190386e8f199716fdd7288f4e05b8c623a8b2 +size 33756 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_6_en.png new file mode 100644 index 0000000000..bf9aed6830 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Day_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:771c01d4563687aff208b0f279c9fb98192b0cd2b2ba56d43627f6c785e92b90 +size 32956 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_2_en.png index 72eca81304..5439563841 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e612d46ec9d5234d26768bf68f7f58877f9344858184bd54c6d6be893e972fa -size 34955 +oid sha256:5b11f9ba2fda961d1571b46c1bcbd7a7ccde6efb4a320ead276779c391421223 +size 8543 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_3_en.png index 8e38bbe539..72eca81304 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb9ef99adff85610ec2808bc468a5f8161db436283e51bde77fc2d6f5b61e214 -size 22502 +oid sha256:8e612d46ec9d5234d26768bf68f7f58877f9344858184bd54c6d6be893e972fa +size 34955 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_4_en.png index 75fdd8172d..8e38bbe539 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f2d01f11bcca2ba3465b6308f7368797c6fd9712bf90f4e9493b30dbece8277 -size 31907 +oid sha256:bb9ef99adff85610ec2808bc468a5f8161db436283e51bde77fc2d6f5b61e214 +size 22502 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_5_en.png index 28fd0cfc21..75fdd8172d 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:484173d52fcb0acff42f62d28f88cd544e205bde49370ccc8230c30b193e1959 -size 31173 +oid sha256:8f2d01f11bcca2ba3465b6308f7368797c6fd9712bf90f4e9493b30dbece8277 +size 31907 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_6_en.png new file mode 100644 index 0000000000..28fd0cfc21 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_LoggedInView_Night_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:484173d52fcb0acff42f62d28f88cd544e205bde49370ccc8230c30b193e1959 +size 31173 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateViewServerUnreachable_Day_0_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateViewServerUnreachable_Day_0_en.png new file mode 100644 index 0000000000..d1e672507d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateViewServerUnreachable_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:433fd969f604ec432cc0b324109c5aa3f60abb44c692c6965731314c66377924 +size 10200 diff --git a/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateViewServerUnreachable_Night_0_en.png b/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateViewServerUnreachable_Night_0_en.png new file mode 100644 index 0000000000..95c1b90315 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/appnav.loggedin_SyncStateViewServerUnreachable_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab1c87fe0b7d5c7e5f1ccad62fba68c2a12607bad342046e58ae11abe981a383 +size 8834 From 362acd6cab8c6b8a1f28ff9c8dd4f6d14860adb4 Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Tue, 25 Aug 2026 21:41:17 +0300 Subject: [PATCH 089/112] Run the database migrations again when a database is upgraded Upgrading over an older install crashed before the login screen with `SQLiteException: no such column: SessionData.position`. The session, cache and push databases were opened at their new schema version but their tables were never migrated. `SqlCipherDriverFactory` overrides `AndroidSqliteDriver.Callback.onUpgrade` so the database key can be replaced, and never called through to the base implementation - which is the only thing that runs `schema.migrate`. Every SQLDelight migration in the app was skipped as a result. The override now calls `super.onUpgrade` after the re-key, so a database that crosses the re-key version is still re-encrypted before its tables are migrated. It moved out of an anonymous object into a named `MigrationCallback` so the behaviour can be tested. Part of #7509 --- libraries/encrypted-db/build.gradle.kts | 4 ++ .../encrypteddb/SqlCipherDriverFactory.kt | 30 +++++++--- .../encrypteddb/MigrationCallbackTest.kt | 55 +++++++++++++++++++ 3 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 libraries/encrypted-db/src/test/kotlin/io/element/encrypteddb/MigrationCallbackTest.kt diff --git a/libraries/encrypted-db/build.gradle.kts b/libraries/encrypted-db/build.gradle.kts index a2150945a5..d48d4aefd1 100644 --- a/libraries/encrypted-db/build.gradle.kts +++ b/libraries/encrypted-db/build.gradle.kts @@ -5,6 +5,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. */ +import extension.testCommonDependencies + plugins { id("io.element.android-library") } @@ -27,4 +29,6 @@ dependencies { implementation(libs.google.tink) implementation(projects.libraries.androidutils) + + testCommonDependencies(libs) } diff --git a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/SqlCipherDriverFactory.kt b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/SqlCipherDriverFactory.kt index 84ab2d2a18..0f2e954b1f 100644 --- a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/SqlCipherDriverFactory.kt +++ b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/SqlCipherDriverFactory.kt @@ -51,12 +51,28 @@ class SqlCipherDriverFactory( is ClientSecret.Passphrase -> secret.formattedAsString().hexToByteArray() } val factory = SupportOpenHelperFactory(key) - return AndroidSqliteDriver(schema = schema, context = context, name = name, factory = factory, callback = object : AndroidSqliteDriver.Callback( - schema - ) { - override fun onUpgrade(db: SupportSQLiteDatabase, oldVersion: Int, newVersion: Int) { - onUpgradeCallback?.invoke(db, oldVersion, newVersion) - } - }) + return AndroidSqliteDriver( + schema = schema, + context = context, + name = name, + factory = factory, + callback = MigrationCallback(schema, onUpgradeCallback), + ) + } +} + +/** + * Runs [onUpgradeCallback] and then the schema migrations themselves. + * + * @param schema The SQLite DB schema, whose migrations are applied by [AndroidSqliteDriver.Callback.onUpgrade]. + * @param onUpgradeCallback Called before the migrations run, so that the database key can be replaced first. + */ +internal class MigrationCallback( + schema: SqlSchema>, + private val onUpgradeCallback: ((driver: SupportSQLiteDatabase, oldVersion: Int, newVersion: Int) -> Unit)?, +) : AndroidSqliteDriver.Callback(schema) { + override fun onUpgrade(db: SupportSQLiteDatabase, oldVersion: Int, newVersion: Int) { + onUpgradeCallback?.invoke(db, oldVersion, newVersion) + super.onUpgrade(db, oldVersion, newVersion) } } diff --git a/libraries/encrypted-db/src/test/kotlin/io/element/encrypteddb/MigrationCallbackTest.kt b/libraries/encrypted-db/src/test/kotlin/io/element/encrypteddb/MigrationCallbackTest.kt new file mode 100644 index 0000000000..5e16184951 --- /dev/null +++ b/libraries/encrypted-db/src/test/kotlin/io/element/encrypteddb/MigrationCallbackTest.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.encrypteddb + +import app.cash.sqldelight.db.AfterVersion +import app.cash.sqldelight.db.QueryResult +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.db.SqlSchema +import com.google.common.truth.Truth.assertThat +import io.mockk.mockk +import org.junit.Test + +class MigrationCallbackTest { + @Test + fun `onUpgrade migrates the schema`() { + val schema = RecordingSchema() + MigrationCallback(schema, onUpgradeCallback = null).onUpgrade(mockk(relaxed = true), 9, 11) + assertThat(schema.migrations).containsExactly(9L to 11L) + } + + @Test + fun `onUpgrade replaces the database key before migrating the schema`() { + val order = mutableListOf() + val schema = RecordingSchema(onMigrate = { order.add("migrate") }) + MigrationCallback(schema, onUpgradeCallback = { _, _, _ -> order.add("rekey") }) + .onUpgrade(mockk(relaxed = true), 9, 11) + assertThat(order).containsExactly("rekey", "migrate").inOrder() + } +} + +private class RecordingSchema( + private val onMigrate: () -> Unit = {}, +) : SqlSchema> { + val migrations = mutableListOf>() + + override val version = 11L + + override fun create(driver: SqlDriver) = QueryResult.Unit + + override fun migrate( + driver: SqlDriver, + oldVersion: Long, + newVersion: Long, + vararg callbacks: AfterVersion, + ): QueryResult.Value { + migrations.add(oldVersion to newVersion) + onMigrate() + return QueryResult.Unit + } +} From 594a5612ad70aa730e7c458033b38da7ae82098a Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Tue, 25 Aug 2026 21:45:59 +0300 Subject: [PATCH 090/112] Register the second sync state preview with konsist --- .../io/element/android/tests/konsist/KonsistPreviewTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt index e066a6b7ec..ed901a99b4 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt @@ -133,6 +133,7 @@ class KonsistPreviewTest { "SecureBackupSetupViewChangePreview", "SelectedUserCannotRemovePreview", "SpaceMembersViewNoHeroesPreview", + "SyncStateViewServerUnreachablePreview", "TextComposerAddCaptionPreview", "TextComposerCaptionPreview", "TextComposerEditCaptionPreview", From 2278116579681b7a129e756d813023783ee311fa Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Tue, 25 Aug 2026 21:52:20 +0300 Subject: [PATCH 091/112] Keep the thread of a message that has been deleted Deleting a message that headed a thread took the thread with it: the "N replies" summary under the message disappeared and there was no way back to the replies, which are still there. Every branch of the timeline content mapper passes `MsgLikeContent`'s thread information on except the redacted one, which returned a bare `RedactedContent` and dropped it. The thread summary lives on `MsgLikeContent` next to `kind`, so it survives the redaction; only the app was throwing it away. `RedactedContent` now carries the thread info like the other contents do, and `EventTimelineItem.threadInfo()` reads it. A deleted message that still heads a thread is also left out of the "N removed messages" group, which would otherwise hide the summary again as soon as three deletions ended up next to each other. Part of #7397 --- .../impl/timeline/groups/Groupability.kt | 2 +- .../timeline/groups/TimelineItemGrouper.kt | 6 ++- .../impl/timeline/TimelinePresenterTest.kt | 2 +- .../factories/TimelineItemsFactoryTest.kt | 42 +++++++++++++++++++ .../groups/TimelineItemGrouperTest.kt | 19 +++++++++ .../RedactedVoiceMessageManagerTest.kt | 2 +- .../DefaultPinnedMessagesBannerFormatter.kt | 2 +- .../impl/DefaultRoomLatestEventFormatter.kt | 2 +- .../impl/DefaultTimelineEventFormatter.kt | 2 +- ...efaultPinnedMessagesBannerFormatterTest.kt | 2 +- .../DefaultRoomLatestEventFormatterTest.kt | 2 +- .../api/timeline/item/event/EventContent.kt | 4 +- .../timeline/item/event/EventTimelineItem.kt | 1 + .../item/event/TimelineEventContentMapper.kt | 2 +- .../reply/InReplyToDetailsPreviewParam.kt | 2 +- .../messages/reply/InReplyToMetadataKtTest.kt | 2 +- .../impl/datasource/EventItemFactory.kt | 2 +- .../impl/datasource/EventItemFactoryTest.kt | 2 +- 18 files changed, 82 insertions(+), 16 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt index 431942378a..3b16f79f79 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt @@ -91,7 +91,7 @@ internal fun MatrixTimelineItem.Event.canBeDisplayedInBubbleBlock(): Boolean { // Can be grouped is FailedToParseMessageLikeContent, is MessageContent, - RedactedContent, + is RedactedContent, is StickerContent, is PollContent, is UnableToDecryptContent, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt index 2e01e53828..b34c66a08d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt @@ -12,6 +12,7 @@ import androidx.annotation.VisibleForTesting import dev.zacsweers.metro.Inject import dev.zacsweers.metro.SingleIn import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.TimelineItemThreadInfo import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.UniqueId @@ -99,7 +100,8 @@ internal const val MIN_REDACTED_RUN_SIZE = 3 * Fold runs of [MIN_REDACTED_RUN_SIZE] or more consecutive deleted (redacted) messages into a single * [TimelineItem.GroupedEvents], so a long stretch of "Message removed" tiles shows as one expandable * "N removed messages" line, like element-web. Shorter runs, and anything that is not a redacted event - * (day dividers included), are left untouched. + * (day dividers included), are left untouched. A deleted message that still heads a thread is left + * untouched too, so that its thread summary stays reachable. * * This runs as the final step of [TimelineItemGrouper.group], after the state and membership events * have been grouped. It is deliberately kept out of the normal grouping: a run needs a higher minimum @@ -134,7 +136,7 @@ internal fun List.collapseRedactedRuns(groupIds: MutableMap MatrixTimelineItem.Event( uniqueId = UniqueId("redacted_$index"), - event = anEventTimelineItem(eventId = EventId("\$R$index"), content = RedactedContent), + event = anEventTimelineItem(eventId = EventId("\$R$index"), content = RedactedContent(threadInfo = null)), ) } ), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt index f93c8f196e..eb3b0529ca 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt @@ -12,10 +12,15 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactory import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition +import io.element.android.features.messages.impl.timeline.model.TimelineItemThreadInfo import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent +import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.EventThreadInfo +import io.element.android.libraries.matrix.api.timeline.item.ThreadSummary import io.element.android.libraries.matrix.api.timeline.item.event.OtherMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.timeline.aMessageContent import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem @@ -128,6 +133,43 @@ class TimelineItemsFactoryTest { } } + @Test + fun `a deleted message keeps the summary of the thread it heads`() = runTest { + val factory = aTimelineItemsFactory( + config = TimelineItemsFactoryConfig( + computeReadReceipts = false, + computeReactions = false, + ) + ) + val items = listOf( + MatrixTimelineItem.Event( + uniqueId = UniqueId("event-0"), + event = anEventTimelineItem( + sender = A_USER_ID, + content = RedactedContent( + threadInfo = EventThreadInfo.ThreadRoot( + summary = ThreadSummary(latestEvent = AsyncData.Uninitialized, numberOfReplies = 3), + ), + ), + ), + ), + ) + factory.timelineItems.test { + factory.replaceWith( + timelineItems = items, + roomMembers = emptyList(), + renderReadReceipts = false, + ) + val threadInfo = awaitItem() + .filterIsInstance() + .single() + .threadInfo + assertThat(threadInfo).isInstanceOf(TimelineItemThreadInfo.ThreadRoot::class.java) + assertThat((threadInfo as TimelineItemThreadInfo.ThreadRoot).summary.numberOfReplies).isEqualTo(3) + cancelAndIgnoreRemainingEvents() + } + } + private suspend fun TestScope.groupPositionsOf(timestamps: List): List { val factory = aTimelineItemsFactory( config = TimelineItemsFactoryConfig( diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt index 005eb5730e..f6ef4f0fc2 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt @@ -15,11 +15,14 @@ import io.element.android.features.messages.impl.timeline.aTimelineItemReactions import io.element.android.features.messages.impl.timeline.model.ReadReceiptData import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts +import io.element.android.features.messages.impl.timeline.model.TimelineItemThreadInfo import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateEventContent import io.element.android.features.messages.impl.timeline.model.virtual.aTimelineItemDaySeparatorModel +import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.avatar.anAvatarData import io.element.android.libraries.matrix.api.core.UniqueId +import io.element.android.libraries.matrix.api.timeline.item.ThreadSummary import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_USER_ID @@ -190,6 +193,22 @@ class TimelineItemGrouperTest { ) } + @Test + fun `a deleted message that still heads a thread is left out of the removed messages group`() { + val threadRoot = aGroupableItem.copy( + id = UniqueId("r1"), + content = TimelineItemRedactedContent, + threadInfo = TimelineItemThreadInfo.ThreadRoot( + summary = ThreadSummary(latestEvent = AsyncData.Uninitialized, numberOfReplies = 2), + latestEventText = null, + ), + ) + val r0 = aGroupableItem.copy(id = UniqueId("r0"), content = TimelineItemRedactedContent) + val r2 = aGroupableItem.copy(id = UniqueId("r2"), content = TimelineItemRedactedContent) + val result = sut.group(listOf(r0, threadRoot, r2)) + assertThat(result).isEqualTo(listOf(r0, threadRoot, r2)) + } + @Test fun `a run of fewer than three redacted events is left untouched`() { val r0 = aGroupableItem.copy(id = UniqueId("r0"), content = TimelineItemRedactedContent) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt index a1ffd1f02b..73cfce399e 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt @@ -88,7 +88,7 @@ fun aRedactedMatrixTimeline(eventId: EventId) = listOf( sender = A_USER_ID, senderProfile = ProfileDetails.Unavailable, timestamp = 9442, - content = RedactedContent, + content = RedactedContent(threadInfo = null), origin = null, timelineItemDebugInfoProvider = { TimelineItemDebugInfo( diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt index 1311c524e4..8025591070 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt @@ -56,7 +56,7 @@ class DefaultPinnedMessagesBannerFormatter( is PollContent -> { content.question.prefixWith(CommonStrings.a11y_poll) } - RedactedContent -> { + is RedactedContent -> { sp.getString(CommonStrings.common_message_removed) } else -> { diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt index e7bbdb1e7f..bd519b60c2 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt @@ -89,7 +89,7 @@ class DefaultRoomLatestEventFormatter( ): CharSequence? { return when (content) { is MessageContent -> content.process(senderDisambiguatedDisplayName, isDmRoom, isOutgoing) - RedactedContent -> { + is RedactedContent -> { val message = sp.getString(CommonStrings.common_message_removed) message.prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing) } diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt index ff5cce7a59..ffb98fe2ec 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt @@ -62,7 +62,7 @@ class DefaultTimelineEventFormatter( is CallNotifyContent -> { sp.getString(CommonStrings.common_call_started) } - RedactedContent, + is RedactedContent, is LegacyCallInviteContent, is StickerContent, is PollContent, diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt index 198da2bf7d..38517dab33 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt @@ -81,7 +81,7 @@ class DefaultPinnedMessagesBannerFormatterTest : RobolectricTest() { fun `Redacted content`() { val expected = "Message removed" val senderName = "Someone" - val message = createRoomEvent(false, senderName, RedactedContent) + val message = createRoomEvent(false, senderName, RedactedContent(threadInfo = null)) val result = formatter.format(message) assertThat(result).isEqualTo(expected) } diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt index ecd0509269..c293b67975 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt @@ -85,7 +85,7 @@ class DefaultRoomLatestEventFormatterTest : RobolectricTest() { val expected = "Message removed" val senderName = "Someone" sequenceOf(false, true).forEach { isDm -> - val message = createLatestEvent(false, senderName, RedactedContent) + val message = createLatestEvent(false, senderName, RedactedContent(threadInfo = null)) val result = formatter.format(message, isDm) if (isDm) { assertThat(result).isEqualTo(expected) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt index c2d388ecb0..132b3b3504 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt @@ -32,7 +32,9 @@ data class MessageContent( val type: MessageType, ) : EventContent -data object RedactedContent : EventContent +data class RedactedContent( + val threadInfo: EventThreadInfo?, +) : EventContent data class StickerContent( val filename: String, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt index 91703f7fa4..8a3221677d 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt @@ -51,6 +51,7 @@ data class EventTimelineItem( is PollContent -> content.threadInfo is StickerContent -> content.threadInfo is UnableToDecryptContent -> content.threadInfo + is RedactedContent -> content.threadInfo else -> null } 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 98efa505d0..c8434e207a 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 @@ -81,7 +81,7 @@ class TimelineEventContentMapper( ) } is MsgLikeKind.Redacted -> { - RedactedContent + RedactedContent(threadInfo = extractThreadInfo(it.content)) } is MsgLikeKind.Poll -> { PollContent( diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsPreviewParam.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsPreviewParam.kt index 1b48e0eb9b..ce9fd884eb 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsPreviewParam.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsPreviewParam.kt @@ -118,7 +118,7 @@ class InReplyToDetailsDisambiguatedPreviewParam : InReplyToDetailsPreviewParam() class InReplyToDetailsInformativePreviewParam : InReplyToDetailsPreviewParam() { override val values: Sequence get() = sequenceOf( - RedactedContent, + RedactedContent(threadInfo = null), UnableToDecryptContent(data = UnableToDecryptContent.Data.Unknown, threadInfo = null), ).map { aInReplyToDetails( diff --git a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt index 3fcb2f8f18..64e647ab19 100644 --- a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt +++ b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt @@ -656,7 +656,7 @@ class InReplyToMetadataKtTest : RobolectricTest() { fun `redacted content`() = runTest { moleculeFlow(RecompositionMode.Immediate) { anInReplyToDetailsReady( - eventContent = RedactedContent + eventContent = RedactedContent(threadInfo = null) ).metadata(hideImage = false) }.test { awaitItem().let { diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactory.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactory.kt index 40a9a0351d..f140fc6628 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactory.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactory.kt @@ -79,7 +79,7 @@ class EventItemFactory( LegacyCallInviteContent, is PollContent, is ProfileChangeContent, - RedactedContent, + is RedactedContent, is RoomMembershipContent, is StateContent, is StickerContent, diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactoryTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactoryTest.kt index 4ed89e8923..56dd5fc937 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactoryTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactoryTest.kt @@ -80,7 +80,7 @@ class EventItemFactoryTest { LegacyCallInviteContent, aPollContent(), aProfileChangeMessageContent(), - RedactedContent, + RedactedContent(threadInfo = null), aRoomMembershipContent( userId = A_USER_ID, ), From 4290a9c3b3b2b6251229c8f89bb997a055bf849e Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Tue, 25 Aug 2026 22:03:14 +0300 Subject: [PATCH 092/112] Keep the formatting of a caption that is being edited Editing the caption of a media message dropped all of its formatting: bold, links and the rest came back as plain text, and saving wrote that plain text over the formatted caption. The composer was being opened on the plain caption. Editing a message body already starts from its HTML when rich text editing is on, and the caption path was the only one that did not - it read `captionOrNull()` and never had the caption's HTML to read, because the timeline content only kept the already-parsed `formattedCaption`. Media contents now carry the caption's HTML alongside the parsed one, the same way text contents carry `htmlBody`, and editing a caption starts from it when rich text editing is on. The save path already sent the composer's HTML, so nothing changes there. Part of #6730 --- .../messages/impl/MessagesPresenter.kt | 10 +++++-- .../TimelineItemContentMessageFactory.kt | 8 ++++++ .../event/TimelineItemAttachmentsContent.kt | 1 + .../model/event/TimelineItemAudioContent.kt | 1 + .../model/event/TimelineItemEventContent.kt | 10 +++++++ .../model/event/TimelineItemFileContent.kt | 1 + .../model/event/TimelineItemGalleryContent.kt | 1 + .../model/event/TimelineItemImageContent.kt | 1 + .../TimelineItemImageContentPreviewParam.kt | 2 ++ .../model/event/TimelineItemStickerContent.kt | 1 + .../model/event/TimelineItemVideoContent.kt | 1 + .../model/event/TimelineItemVoiceContent.kt | 1 + .../messages/impl/MessagesPresenterTest.kt | 27 +++++++++++++++++++ .../TimelineItemContentMessageFactoryTest.kt | 2 ++ 14 files changed, 65 insertions(+), 2 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 3ba8099ecb..6a019d865b 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 @@ -51,6 +51,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent import io.element.android.features.messages.impl.timeline.model.event.captionOrNull +import io.element.android.features.messages.impl.timeline.model.event.htmlCaptionOrNull import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState import io.element.android.features.messages.impl.voicemessages.composer.DefaultVoiceMessageComposerPresenter import io.element.android.features.roomcall.api.RoomCallState @@ -377,7 +378,7 @@ class MessagesPresenter( TimelineItemAction.Edit, TimelineItemAction.EditPoll -> handleActionEdit(targetEvent, composerState, enableTextFormatting) TimelineItemAction.AddCaption -> handleActionAddCaption(targetEvent, composerState) - TimelineItemAction.EditCaption -> handleActionEditCaption(targetEvent, composerState) + TimelineItemAction.EditCaption -> handleActionEditCaption(targetEvent, composerState, enableTextFormatting) TimelineItemAction.RemoveCaption -> handleRemoveCaption(targetEvent) TimelineItemAction.Reply -> handleActionReply(targetEvent, composerState, timelineProtectionState) TimelineItemAction.ReplyInThread -> { @@ -546,10 +547,15 @@ class MessagesPresenter( private suspend fun handleActionEditCaption( targetEvent: TimelineItem.Event, composerState: MessageComposerState, + enableTextFormatting: Boolean, ) { val composerMode = MessageComposerMode.EditCaption( eventOrTransactionId = targetEvent.eventOrTransactionId, - content = targetEvent.content.captionOrNull().orEmpty(), + content = if (enableTextFormatting) { + targetEvent.content.htmlCaptionOrNull() ?: targetEvent.content.captionOrNull() + } else { + targetEvent.content.captionOrNull() + }.orEmpty(), ) composerState.eventSink( MessageComposerEvent.SetMode(composerMode) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index 040d0450ff..75966db1a3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -108,6 +108,7 @@ class TimelineItemContentMessageFactory( fileSize = messageType.info?.size ?: 0, caption = messageType.caption?.trimEnd(), formattedCaption = formattedCaption, + htmlCaption = messageType.formattedCaption?.body, isEdited = content.isEdited, mediaSource = messageType.source, thumbnailSource = messageType.info?.thumbnailSource, @@ -132,6 +133,7 @@ class TimelineItemContentMessageFactory( fileSize = messageType.info?.size ?: 0, caption = messageType.caption?.trimEnd(), formattedCaption = formattedCaption, + htmlCaption = messageType.formattedCaption?.body, isEdited = content.isEdited, mediaSource = messageType.source, thumbnailSource = messageType.info?.thumbnailSource, @@ -174,6 +176,7 @@ class TimelineItemContentMessageFactory( fileSize = messageType.info?.size ?: 0, caption = messageType.caption?.trimEnd(), formattedCaption = formattedCaption, + htmlCaption = messageType.formattedCaption?.body, isEdited = content.isEdited, thumbnailSource = messageType.info?.thumbnailSource, mediaSource = messageType.source, @@ -198,6 +201,7 @@ class TimelineItemContentMessageFactory( fileSize = messageType.info?.size ?: 0, caption = messageType.caption?.trimEnd(), formattedCaption = formattedCaption, + htmlCaption = messageType.formattedCaption?.body, isEdited = content.isEdited, mediaSource = messageType.source, duration = messageType.info?.duration ?: Duration.ZERO, @@ -216,6 +220,7 @@ class TimelineItemContentMessageFactory( fileSize = messageType.info?.size ?: 0, caption = messageType.caption?.trimEnd(), formattedCaption = formattedCaption, + htmlCaption = messageType.formattedCaption?.body, isEdited = content.isEdited, mediaSource = messageType.source, duration = messageType.info?.duration ?: Duration.ZERO, @@ -235,6 +240,7 @@ class TimelineItemContentMessageFactory( fileSize = messageType.info?.size ?: 0, caption = messageType.caption?.trimEnd(), formattedCaption = formattedCaption, + htmlCaption = messageType.formattedCaption?.body, isEdited = content.isEdited, thumbnailSource = messageType.info?.thumbnailSource, mediaSource = messageType.source, @@ -345,6 +351,7 @@ class TimelineItemContentMessageFactory( body = messageType.body, caption = messageType.body.trimEnd().takeIf { it.isNotEmpty() }, formattedCaption = formattedCaption, + htmlCaption = messageType.formatted?.body, isEdited = content.isEdited, items = galleryItems.toImmutableList(), ) @@ -402,6 +409,7 @@ class TimelineItemContentMessageFactory( body = messageType.body, caption = messageType.body.trimEnd().takeIf { it.isNotEmpty() }, formattedCaption = formattedCaption, + htmlCaption = messageType.formatted?.body, isEdited = content.isEdited, attachments = attachments.toImmutableList(), ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAttachmentsContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAttachmentsContent.kt index 66d28598a1..dd3578ea61 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAttachmentsContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAttachmentsContent.kt @@ -14,6 +14,7 @@ data class TimelineItemAttachmentsContent( val body: String, val caption: String?, val formattedCaption: CharSequence?, + val htmlCaption: String? = null, override val isEdited: Boolean, val attachments: ImmutableList, ) : TimelineItemEventContent, TimelineItemEventMutableContent { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt index f28de4f673..8c191ef140 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt @@ -17,6 +17,7 @@ data class TimelineItemAudioContent( override val fileSize: Long?, override val caption: String?, override val formattedCaption: CharSequence?, + override val htmlCaption: String? = null, override val isEdited: Boolean, val duration: Duration, override val mediaSource: MediaSource, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt index 01146a070b..efa73b91fc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt @@ -33,6 +33,9 @@ sealed interface TimelineItemEventContentWithAttachment : val fileSize: Long? val caption: String? val formattedCaption: CharSequence? + + /** The caption as it was written, in HTML, so that editing it can start from the formatted text. */ + val htmlCaption: String? val mediaSource: MediaSource val mimeType: String val formattedFileSize: String @@ -127,6 +130,13 @@ fun TimelineItemEventContent.formattedCaptionOrNull(): CharSequence? = when (thi else -> null } +fun TimelineItemEventContent.htmlCaptionOrNull(): String? = when (this) { + is TimelineItemEventContentWithAttachment -> htmlCaption + is TimelineItemGalleryContent -> htmlCaption + is TimelineItemAttachmentsContent -> htmlCaption + else -> null +} + fun TimelineItemEventContentWithAttachment.duration(): Duration? { return when (this) { is TimelineItemAudioContent -> duration diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContent.kt index 680797e36b..36beeeb91d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemFileContent.kt @@ -16,6 +16,7 @@ data class TimelineItemFileContent( override val fileSize: Long?, override val caption: String?, override val formattedCaption: CharSequence?, + override val htmlCaption: String? = null, override val isEdited: Boolean, override val mediaSource: MediaSource, val thumbnailSource: MediaSource?, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemGalleryContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemGalleryContent.kt index f376a2a00a..840f19488d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemGalleryContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemGalleryContent.kt @@ -16,6 +16,7 @@ data class TimelineItemGalleryContent( val body: String, val caption: String?, val formattedCaption: CharSequence?, + val htmlCaption: String? = null, override val isEdited: Boolean, val items: ImmutableList, ) : TimelineItemEventContent, TimelineItemEventMutableContent { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt index 4828f22ba8..5e78adc945 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt @@ -19,6 +19,7 @@ data class TimelineItemImageContent( override val fileSize: Long?, override val caption: String?, override val formattedCaption: CharSequence?, + override val htmlCaption: String? = null, override val isEdited: Boolean, override val mediaSource: MediaSource, val thumbnailSource: MediaSource?, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentPreviewParam.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentPreviewParam.kt index cd93c7c006..316b2ab79b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentPreviewParam.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentPreviewParam.kt @@ -28,6 +28,7 @@ fun aTimelineItemImageContent( blurhash: String? = A_BLUR_HASH, filename: String = "A picture.jpg", caption: String? = null, + htmlCaption: String? = null, width: Int? = null, height: Int? = 300, ) = TimelineItemImageContent( @@ -35,6 +36,7 @@ fun aTimelineItemImageContent( fileSize = 4 * 1024 * 1024L, caption = caption, formattedCaption = null, + htmlCaption = htmlCaption, isEdited = false, mediaSource = MediaSource(""), thumbnailSource = null, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStickerContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStickerContent.kt index 9e2f08d5c7..65585bc028 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStickerContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStickerContent.kt @@ -15,6 +15,7 @@ data class TimelineItemStickerContent( override val fileSize: Long?, override val caption: String?, override val formattedCaption: CharSequence?, + override val htmlCaption: String? = null, override val isEdited: Boolean, override val mediaSource: MediaSource, val thumbnailSource: MediaSource?, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContent.kt index 80635c6e18..a1c8e279cd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContent.kt @@ -16,6 +16,7 @@ data class TimelineItemVideoContent( override val fileSize: Long?, override val caption: String?, override val formattedCaption: CharSequence?, + override val htmlCaption: String? = null, override val isEdited: Boolean, val duration: Duration, override val mediaSource: MediaSource, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContent.kt index 29a432e6bd..806a703ea5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContent.kt @@ -19,6 +19,7 @@ data class TimelineItemVoiceContent( override val fileSize: Long?, override val caption: String?, override val formattedCaption: CharSequence?, + override val htmlCaption: String? = null, override val isEdited: Boolean, val duration: Duration, override val mediaSource: MediaSource, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index 227088f78c..4c0abc0a34 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -989,6 +989,33 @@ class MessagesPresenterTest { } } + @Test + fun `present - handle action edit caption starts from the formatted caption`() = runTest { + val messageEvent = aMessageEvent( + content = aTimelineItemImageContent( + caption = "Hello world", + htmlCaption = "Hello world", + ) + ) + val composerRecorder = EventsRecorder() + val presenter = createMessagesPresenter( + messageComposerPresenter = { aMessageComposerState(eventSink = composerRecorder, showTextFormatting = true) }, + ) + presenter.testWithLifecycleOwner { + val initialState = awaitItem() + initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.EditCaption, messageEvent)) + awaitItem() + composerRecorder.assertSingle( + MessageComposerEvent.SetMode( + composerMode = MessageComposerMode.EditCaption( + eventOrTransactionId = AN_EVENT_ID.toEventOrTransactionId(), + content = "Hello world", + ) + ) + ) + } + } + @Test fun `present - handle action add caption`() = runTest { val composerRecorder = EventsRecorder() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt index fc01184764..1c9de095d3 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt @@ -297,6 +297,7 @@ class TimelineItemContentMessageFactoryTest : RobolectricTest() { fileSize = 555L, caption = "body.mp4 caption", formattedCaption = SpannedString("formatted"), + htmlCaption = "formatted", isEdited = true, duration = 1.minutes, mediaSource = MediaSource(url = "url", json = null), @@ -539,6 +540,7 @@ class TimelineItemContentMessageFactoryTest : RobolectricTest() { fileSize = 888L, caption = "body.jpg caption", formattedCaption = SpannedString("formatted"), + htmlCaption = "formatted", isEdited = true, mediaSource = MediaSource(url = "url", json = null), thumbnailSource = MediaSource("url_thumbnail"), From 0801a415b59d89504a1815601eca8730c9de0dfc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Aug 2026 12:11:04 +0200 Subject: [PATCH 093/112] Add a FileViewerEntryPoint to render in-memory content as a file Introduce io.element.android.libraries.mediaviewer.api.FileViewerEntryPoint, a reusable screen which renders content that is not a Matrix media the same way the media viewer renders a file, with Share and Download actions. The caller provides a filename, a mime type and the whole content as a String. The node writes it to a temporary file in the cache directory, then reuses LocalMediaView (so text content goes through TextFileViewer) and LocalMediaActions for sharing and saving on disk. The node is contributed to AppScope so that the screen can be used from any graph, since MediaViewerNode itself is bound to RoomScope and depends on the timeline data sources. Also route MimeTypes.Json to TextFileView in LocalMediaView, so that a json file is rendered as text instead of falling back to the generic file card. Co-Authored-By: Claude Opus 5 (1M context) --- .../mediaviewer/api/FileViewerEntryPoint.kt | 44 ++++++ .../fileviewer/DefaultFileViewerEntryPoint.kt | 30 ++++ .../impl/fileviewer/FileViewerEvent.kt | 13 ++ .../impl/fileviewer/FileViewerNode.kt | 47 ++++++ .../impl/fileviewer/FileViewerPresenter.kt | 143 ++++++++++++++++++ .../impl/fileviewer/FileViewerState.kt | 19 +++ .../fileviewer/FileViewerStatePreviewParam.kt | 62 ++++++++ .../impl/fileviewer/FileViewerView.kt | 134 ++++++++++++++++ .../mediaviewer/impl/local/LocalMediaView.kt | 2 +- .../DefaultFileViewerEntryPointTest.kt | 73 +++++++++ .../fileviewer/FileViewerPresenterTest.kt | 100 ++++++++++++ .../impl/fileviewer/FileViewerViewTest.kt | 87 +++++++++++ .../test/FakeFileViewerEntryPoint.kt | 22 +++ 13 files changed, 775 insertions(+), 1 deletion(-) create mode 100644 libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/FileViewerEntryPoint.kt create mode 100644 libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/DefaultFileViewerEntryPoint.kt create mode 100644 libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerEvent.kt create mode 100644 libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerNode.kt create mode 100644 libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerPresenter.kt create mode 100644 libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerState.kt create mode 100644 libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerStatePreviewParam.kt create mode 100644 libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerView.kt create mode 100644 libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/DefaultFileViewerEntryPointTest.kt create mode 100644 libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerPresenterTest.kt create mode 100644 libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerViewTest.kt create mode 100644 libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeFileViewerEntryPoint.kt diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/FileViewerEntryPoint.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/FileViewerEntryPoint.kt new file mode 100644 index 0000000000..8f90a5802b --- /dev/null +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/FileViewerEntryPoint.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.api + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import io.element.android.libraries.architecture.FeatureEntryPoint +import io.element.android.libraries.architecture.NodeInputs + +/** + * Renders content which is not a Matrix media as a file, the same way the media viewer does. + * + * The content is provided by the caller and written to a temporary file, so that it can be + * displayed, shared and saved on disk. + */ +interface FileViewerEntryPoint : FeatureEntryPoint { + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + callback: Callback, + ): Node + + /** + * @param filename the name of the file, used as the screen title and for the shared and saved files. + * @param mimeType the mime type of [content], it drives how the content is rendered. + * @param content the whole content of the file. + */ + data class Params( + val filename: String, + val mimeType: String, + val content: String, + ) : NodeInputs + + interface Callback : Plugin { + fun onDone() + } +} diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/DefaultFileViewerEntryPoint.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/DefaultFileViewerEntryPoint.kt new file mode 100644 index 0000000000..2692e8461d --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/DefaultFileViewerEntryPoint.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.impl.fileviewer + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.mediaviewer.api.FileViewerEntryPoint + +@ContributesBinding(AppScope::class) +class DefaultFileViewerEntryPoint : FileViewerEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: FileViewerEntryPoint.Params, + callback: FileViewerEntryPoint.Callback, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf(params, callback), + ) + } +} diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerEvent.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerEvent.kt new file mode 100644 index 0000000000..f36e1b87cf --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerEvent.kt @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.impl.fileviewer + +sealed interface FileViewerEvent { + data object Share : FileViewerEvent + data object SaveOnDisk : FileViewerEvent +} diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerNode.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerNode.kt new file mode 100644 index 0000000000..ee1b35c990 --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerNode.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.impl.fileviewer + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.Assisted +import dev.zacsweers.metro.AssistedInject +import io.element.android.annotations.ContributesNode +import io.element.android.features.viewfolder.api.TextFileViewer +import io.element.android.libraries.architecture.callback +import io.element.android.libraries.architecture.inputs +import io.element.android.libraries.mediaviewer.api.FileViewerEntryPoint + +@ContributesNode(AppScope::class) +@AssistedInject +class FileViewerNode( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + presenterFactory: FileViewerPresenter.Factory, + private val textFileViewer: TextFileViewer, +) : Node(buildContext, plugins = plugins) { + private val callback: FileViewerEntryPoint.Callback = callback() + private val inputs = inputs() + + private val presenter = presenterFactory.create(params = inputs) + + @Composable + override fun View(modifier: Modifier) { + val state = presenter.present() + FileViewerView( + state = state, + textFileViewer = textFileViewer, + onBackClick = callback::onDone, + modifier = modifier, + ) + } +} diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerPresenter.kt new file mode 100644 index 0000000000..61be47ebb2 --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerPresenter.kt @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.impl.fileviewer + +import android.content.ActivityNotFoundException +import android.content.Context +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 androidx.compose.runtime.rememberCoroutineScope +import androidx.core.net.toUri +import dev.zacsweers.metro.Assisted +import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject +import io.element.android.libraries.androidutils.filesize.FileSizeFormatter +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.runCatchingUpdatingState +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher +import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage +import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState +import io.element.android.libraries.di.annotations.ApplicationContext +import io.element.android.libraries.mediaviewer.api.FileViewerEntryPoint +import io.element.android.libraries.mediaviewer.api.MediaInfo +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractor +import io.element.android.libraries.mediaviewer.impl.local.LocalMediaActions +import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.io.File +import io.element.android.libraries.androidutils.R as UtilsR + +@AssistedInject +class FileViewerPresenter( + @Assisted private val params: FileViewerEntryPoint.Params, + @ApplicationContext private val context: Context, + private val coroutineDispatchers: CoroutineDispatchers, + private val fileSizeFormatter: FileSizeFormatter, + private val fileExtensionExtractor: FileExtensionExtractor, + private val localMediaActions: LocalMediaActions, +) : Presenter { + @AssistedFactory + fun interface Factory { + fun create(params: FileViewerEntryPoint.Params): FileViewerPresenter + } + + // Use a local snackbarDispatcher, because this screen can be rendered from any Node. + private val snackbarDispatcher = SnackbarDispatcher() + + @Composable + override fun present(): FileViewerState { + val coroutineScope = rememberCoroutineScope() + val localMedia = remember { mutableStateOf>(AsyncData.Uninitialized) } + val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() + + localMediaActions.Configure() + + LaunchedEffect(Unit) { + suspend { createLocalMedia() }.runCatchingUpdatingState(localMedia) + } + + fun handleEvent(event: FileViewerEvent) { + when (event) { + FileViewerEvent.SaveOnDisk -> coroutineScope.saveOnDisk(localMedia.value) + FileViewerEvent.Share -> coroutineScope.share(localMedia.value) + } + } + + return FileViewerState( + filename = params.filename, + localMedia = localMedia.value, + snackbarMessage = snackbarMessage, + eventSink = ::handleEvent, + ) + } + + /** + * Write the content to a temporary file, so that it can be rendered, shared and saved on disk. + * The file is always written to the same location, so it does not need to be cleaned up. + */ + private suspend fun createLocalMedia(): LocalMedia = withContext(coroutineDispatchers.io) { + val folder = File(context.cacheDir, "temp/fileviewer").apply { mkdirs() } + val file = File(folder, params.filename) + file.writeText(params.content) + LocalMedia( + uri = file.toUri(), + info = MediaInfo( + filename = params.filename, + caption = null, + mimeType = params.mimeType, + fileSize = file.length(), + formattedFileSize = fileSizeFormatter.format(file.length()), + fileExtension = fileExtensionExtractor.extractFromName(params.filename), + senderId = null, + senderName = null, + senderAvatar = null, + dateSent = null, + dateSentFull = null, + waveform = null, + duration = null, + ), + ) + } + + private fun CoroutineScope.saveOnDisk(localMedia: AsyncData) = launch { + if (localMedia is AsyncData.Success) { + localMediaActions.saveOnDisk(localMedia.data) + .onSuccess { + snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_file_saved_on_disk_android)) + } + .onFailure { + snackbarDispatcher.post(SnackbarMessage(mediaActionsError(it))) + } + } + } + + private fun CoroutineScope.share(localMedia: AsyncData) = launch { + if (localMedia is AsyncData.Success) { + localMediaActions.share(localMedia.data) + .onFailure { + snackbarDispatcher.post(SnackbarMessage(mediaActionsError(it))) + } + } + } + + private fun mediaActionsError(throwable: Throwable): Int { + return if (throwable is ActivityNotFoundException) { + UtilsR.string.error_no_compatible_app_found + } else { + CommonStrings.error_unknown + } + } +} diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerState.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerState.kt new file mode 100644 index 0000000000..0edc198205 --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerState.kt @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.impl.fileviewer + +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage +import io.element.android.libraries.mediaviewer.api.local.LocalMedia + +data class FileViewerState( + val filename: String, + val localMedia: AsyncData, + val snackbarMessage: SnackbarMessage?, + val eventSink: (FileViewerEvent) -> Unit, +) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerStatePreviewParam.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerStatePreviewParam.kt new file mode 100644 index 0000000000..3f48dbdc31 --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerStatePreviewParam.kt @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.impl.fileviewer + +import android.net.Uri +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.mediaviewer.api.MediaInfo +import io.element.android.libraries.mediaviewer.api.local.LocalMedia + +private const val A_FILENAME = "push_rules@alice_server.org.json" + +open class FileViewerStatePreviewParam : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aFileViewerState(), + aFileViewerState(localMedia = AsyncData.Failure(Exception("A failure"))), + aFileViewerState( + localMedia = AsyncData.Success( + LocalMedia( + uri = Uri.EMPTY, + info = aJsonMediaInfo(), + ) + ) + ), + ) +} + +fun aFileViewerState( + filename: String = A_FILENAME, + localMedia: AsyncData = AsyncData.Uninitialized, + eventSink: (FileViewerEvent) -> Unit = {}, +) = FileViewerState( + filename = filename, + localMedia = localMedia, + snackbarMessage = null, + eventSink = eventSink, +) + +fun aJsonMediaInfo( + filename: String = A_FILENAME, +) = MediaInfo( + filename = filename, + caption = null, + mimeType = MimeTypes.Json, + fileSize = 2 * 1024, + formattedFileSize = "2kB", + fileExtension = "json", + senderId = null, + senderName = null, + senderAvatar = null, + dateSent = null, + dateSentFull = null, + waveform = null, + duration = null, +) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerView.kt new file mode 100644 index 0000000000..613e3af269 --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerView.kt @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +@file:OptIn(ExperimentalMaterial3Api::class) + +package io.element.android.libraries.mediaviewer.impl.fileviewer + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.PreviewParameter +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.viewfolder.api.TextFileViewer +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.designsystem.components.async.AsyncFailure +import io.element.android.libraries.designsystem.components.async.AsyncLoading +import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.IconButton +import io.element.android.libraries.designsystem.theme.components.Scaffold +import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost +import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState +import io.element.android.libraries.mediaviewer.impl.local.LocalMediaView +import io.element.android.libraries.mediaviewer.impl.util.bgCanvasWithTransparency +import io.element.android.libraries.ui.strings.CommonStrings + +@Composable +fun FileViewerView( + state: FileViewerState, + textFileViewer: TextFileViewer, + onBackClick: () -> Unit, + modifier: Modifier = Modifier, +) { + val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage) + Scaffold( + modifier = modifier, + topBar = { + FileViewerTopBar( + filename = state.filename, + actionsEnabled = state.localMedia.isSuccess(), + onBackClick = onBackClick, + onShareClick = { state.eventSink(FileViewerEvent.Share) }, + onSaveClick = { state.eventSink(FileViewerEvent.SaveOnDisk) }, + ) + }, + snackbarHost = { SnackbarHost(snackbarHostState) }, + ) { padding -> + when (val localMedia = state.localMedia) { + AsyncData.Uninitialized, + is AsyncData.Loading -> AsyncLoading( + modifier = Modifier.padding(padding), + ) + is AsyncData.Failure -> AsyncFailure( + modifier = Modifier.padding(padding), + throwable = localMedia.error, + onRetry = null, + ) + is AsyncData.Success -> { + // Do not apply the padding, the content is rendered behind the translucent top bar, + // the same way the media viewer does. + LocalMediaView( + localMedia = localMedia.data, + bottomPaddingInPixels = 0, + audioFocus = null, + onClick = {}, + onOpenWith = null, + textFileViewer = textFileViewer, + forPreview = false, + modifier = Modifier.fillMaxSize(), + ) + } + } + } +} + +@Composable +private fun FileViewerTopBar( + filename: String, + actionsEnabled: Boolean, + onBackClick: () -> Unit, + onShareClick: () -> Unit, + onSaveClick: () -> Unit, +) { + TopAppBar( + titleStr = filename, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = bgCanvasWithTransparency, + ), + navigationIcon = { BackButton(onClick = onBackClick) }, + actions = { + IconButton( + onClick = onShareClick, + enabled = actionsEnabled, + ) { + Icon( + imageVector = CompoundIcons.ShareAndroid(), + contentDescription = stringResource(id = CommonStrings.action_share), + ) + } + IconButton( + onClick = onSaveClick, + enabled = actionsEnabled, + ) { + Icon( + imageVector = CompoundIcons.Download(), + contentDescription = stringResource(id = CommonStrings.action_download), + ) + } + }, + ) +} + +@PreviewsDayNight +@Composable +internal fun FileViewerViewPreview( + @PreviewParameter(FileViewerStatePreviewParam::class) state: FileViewerState, +) = ElementPreview { + FileViewerView( + state = state, + textFileViewer = { _, _ -> }, + onBackClick = {}, + ) +} diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt index 01b44f1246..e370361696 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt @@ -59,7 +59,7 @@ fun LocalMediaView( forPreview = forPreview, modifier = modifier, ) - mimeType == MimeTypes.PlainText -> TextFileView( + mimeType == MimeTypes.PlainText || mimeType == MimeTypes.Json -> TextFileView( localMedia = localMedia, textFileViewer = textFileViewer, modifier = modifier, diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/DefaultFileViewerEntryPointTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/DefaultFileViewerEntryPointTest.kt new file mode 100644 index 0000000000..2a84ad00b8 --- /dev/null +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/DefaultFileViewerEntryPointTest.kt @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.impl.fileviewer + +import android.content.Context +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.testing.junit4.util.MainDispatcherRule +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter +import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.mediaviewer.api.FileViewerEntryPoint +import io.element.android.libraries.mediaviewer.test.FakeLocalMediaActions +import io.element.android.libraries.mediaviewer.test.util.FileExtensionExtractorWithoutValidation +import io.element.android.tests.testutils.lambda.lambdaError +import io.element.android.tests.testutils.node.TestParentNode +import io.element.android.tests.testutils.testCoroutineDispatchers +import io.mockk.mockk +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test + +class DefaultFileViewerEntryPointTest { + @get:Rule + val instantTaskExecutorRule = InstantTaskExecutorRule() + + @get:Rule + val mainDispatcherRule = MainDispatcherRule() + + @Test + fun `test node builder`() = runTest { + val entryPoint = DefaultFileViewerEntryPoint() + val parentNode = TestParentNode.create { buildContext, plugins -> + FileViewerNode( + buildContext = buildContext, + plugins = plugins, + presenterFactory = { nodeParams -> + FileViewerPresenter( + params = nodeParams, + context = mockk(relaxed = true), + coroutineDispatchers = testCoroutineDispatchers(), + fileSizeFormatter = FakeFileSizeFormatter(), + fileExtensionExtractor = FileExtensionExtractorWithoutValidation(), + localMediaActions = FakeLocalMediaActions(), + ) + }, + textFileViewer = { _, _ -> lambdaError() }, + ) + } + val params = FileViewerEntryPoint.Params( + filename = "a file.json", + mimeType = MimeTypes.Json, + content = "{}", + ) + val callback = object : FileViewerEntryPoint.Callback { + override fun onDone() = lambdaError() + } + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + callback = callback, + ) + assertThat(result).isInstanceOf(FileViewerNode::class.java) + assertThat(result.plugins).contains(params) + assertThat(result.plugins).contains(callback) + } +} diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerPresenterTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerPresenterTest.kt new file mode 100644 index 0000000000..7a8a85d69e --- /dev/null +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerPresenterTest.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.impl.fileviewer + +import android.content.ActivityNotFoundException +import androidx.core.net.toFile +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.mediaviewer.api.FileViewerEntryPoint +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.impl.local.LocalMediaActions +import io.element.android.libraries.mediaviewer.test.FakeLocalMediaActions +import io.element.android.libraries.mediaviewer.test.util.FileExtensionExtractorWithoutValidation +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.tests.testutils.consumeItemsUntilPredicate +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.robolectric.RobolectricTest +import io.element.android.tests.testutils.test +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.robolectric.RuntimeEnvironment +import io.element.android.libraries.androidutils.R as UtilsR + +private const val A_FILENAME = "push_rules@alice_server.org.json" +private const val A_CONTENT = """{"global":{"override":[]}}""" + +class FileViewerPresenterTest : RobolectricTest() { + @Test + fun `present - writes the content to a file and exposes it as a local media`() = runTest { + val presenter = createFileViewerPresenter() + presenter.test { + assertThat(awaitItem().localMedia).isEqualTo(AsyncData.Uninitialized) + consumeItemsUntilPredicate { it.localMedia.isSuccess() }.last().also { state -> + assertThat(state.filename).isEqualTo(A_FILENAME) + val localMedia = state.localMedia.dataOrNull()!! + assertThat(localMedia.info.filename).isEqualTo(A_FILENAME) + assertThat(localMedia.info.mimeType).isEqualTo(MimeTypes.Json) + assertThat(localMedia.info.fileExtension).isEqualTo("json") + val file = localMedia.uri.toFile() + assertThat(file.name).isEqualTo(A_FILENAME) + assertThat(file.readText()).isEqualTo(A_CONTENT) + } + } + } + + @Test + fun `present - SaveOnDisk event saves the file and posts a snackbar message`() = runTest { + val saveOnDiskResult = lambdaRecorder> { Result.success(Unit) } + val presenter = createFileViewerPresenter( + localMediaActions = FakeLocalMediaActions(saveOnDiskResult = saveOnDiskResult), + ) + presenter.test { + consumeItemsUntilPredicate { it.localMedia.isSuccess() }.last().eventSink(FileViewerEvent.SaveOnDisk) + consumeItemsUntilPredicate { it.snackbarMessage != null }.last().also { state -> + assertThat(state.snackbarMessage?.messageResId).isEqualTo(CommonStrings.common_file_saved_on_disk_android) + } + saveOnDiskResult.assertions().isCalledOnce() + } + } + + @Test + fun `present - Share event failure posts a snackbar message`() = runTest { + val shareResult = lambdaRecorder> { Result.failure(ActivityNotFoundException()) } + val presenter = createFileViewerPresenter( + localMediaActions = FakeLocalMediaActions(shareResult = shareResult), + ) + presenter.test { + consumeItemsUntilPredicate { it.localMedia.isSuccess() }.last().eventSink(FileViewerEvent.Share) + consumeItemsUntilPredicate { it.snackbarMessage != null }.last().also { state -> + assertThat(state.snackbarMessage?.messageResId).isEqualTo(UtilsR.string.error_no_compatible_app_found) + } + shareResult.assertions().isCalledOnce() + } + } + + private fun TestScope.createFileViewerPresenter( + params: FileViewerEntryPoint.Params = FileViewerEntryPoint.Params( + filename = A_FILENAME, + mimeType = MimeTypes.Json, + content = A_CONTENT, + ), + localMediaActions: LocalMediaActions = FakeLocalMediaActions(), + ) = FileViewerPresenter( + params = params, + context = RuntimeEnvironment.getApplication(), + coroutineDispatchers = testCoroutineDispatchers(), + fileSizeFormatter = FakeFileSizeFormatter(), + fileExtensionExtractor = FileExtensionExtractorWithoutValidation(), + localMediaActions = localMediaActions, + ) +} diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerViewTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerViewTest.kt new file mode 100644 index 0000000000..96f5de7007 --- /dev/null +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/fileviewer/FileViewerViewTest.kt @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +@file:OptIn(ExperimentalTestApi::class) + +package io.element.android.libraries.mediaviewer.impl.fileviewer + +import android.net.Uri +import androidx.activity.ComponentActivity +import androidx.annotation.StringRes +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.hasClickAction +import androidx.compose.ui.test.hasContentDescription +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.tests.testutils.EnsureNeverCalled +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.ensureCalledOnce +import io.element.android.tests.testutils.pressBack +import io.element.android.tests.testutils.robolectric.RobolectricTest +import org.junit.Test + +class FileViewerViewTest : RobolectricTest() { + @Test + fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder(expectEvents = false) + ensureCalledOnce { + setFileViewerView( + state = aFileViewerState(eventSink = eventsRecorder), + onBackClick = it, + ) + pressBack() + } + } + + @Test + fun `clicking on share emits the expected event`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() + setFileViewerView( + state = aFileViewerState( + localMedia = AsyncData.Success(LocalMedia(Uri.EMPTY, aJsonMediaInfo())), + eventSink = eventsRecorder, + ), + ) + clickOnContentDescription(CommonStrings.action_share) + eventsRecorder.assertSingle(FileViewerEvent.Share) + } + + @Test + fun `clicking on download emits the expected event`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() + setFileViewerView( + state = aFileViewerState( + localMedia = AsyncData.Success(LocalMedia(Uri.EMPTY, aJsonMediaInfo())), + eventSink = eventsRecorder, + ), + ) + clickOnContentDescription(CommonStrings.action_download) + eventsRecorder.assertSingle(FileViewerEvent.SaveOnDisk) + } +} + +private fun AndroidComposeUiTest.clickOnContentDescription(@StringRes res: Int) { + val text = activity!!.getString(res) + onNode(hasContentDescription(text) and hasClickAction()).performClick() +} + +private fun AndroidComposeUiTest.setFileViewerView( + state: FileViewerState, + onBackClick: () -> Unit = EnsureNeverCalled(), +) { + setContent { + FileViewerView( + state = state, + textFileViewer = { _, _ -> }, + onBackClick = onBackClick, + ) + } +} diff --git a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeFileViewerEntryPoint.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeFileViewerEntryPoint.kt new file mode 100644 index 0000000000..0379bff47d --- /dev/null +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeFileViewerEntryPoint.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.libraries.mediaviewer.api.FileViewerEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeFileViewerEntryPoint : FileViewerEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: FileViewerEntryPoint.Params, + callback: FileViewerEntryPoint.Callback, + ): Node = lambdaError() +} From 373d2f1eb5c13fb8599f121315bbbd65e375eadf Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Aug 2026 12:11:16 +0200 Subject: [PATCH 094/112] Add a Push rules entry to the developer options Add a "Push rules" item in the Notifications section of the developer options, above "Push history". It displays the raw push rules of the account, which were only available as a rageshake attachment so far. The content is read using NotificationSettingsService.getRawPushRules(), then rendered by the new FileViewerEntryPoint, so that the screen looks like the media viewer rendering a file, with Share and Download actions on a push_rules.json file. The user id contains a colon, which is not a valid file name character on all the file systems, so it is replaced by an underscore. Co-Authored-By: Claude Opus 5 (1M context) --- features/preferences/impl/build.gradle.kts | 2 + .../preferences/impl/PreferencesFlowNode.kt | 30 ++++++++++ .../impl/developer/DeveloperSettingsEvent.kt | 2 + .../developer/DeveloperSettingsNavigator.kt | 15 +++++ .../impl/developer/DeveloperSettingsNode.kt | 11 +++- .../developer/DeveloperSettingsPresenter.kt | 43 ++++++++++++++- .../impl/developer/DeveloperSettingsState.kt | 5 +- .../DeveloperSettingsStatePreviewParam.kt | 5 ++ .../impl/developer/DeveloperSettingsView.kt | 24 +++++++- .../impl/DefaultPreferencesEntryPointTest.kt | 2 + .../DeveloperSettingsPresenterTest.kt | 55 +++++++++++++++++++ .../developer/DeveloperSettingsViewTest.kt | 13 +++++ 12 files changed, 200 insertions(+), 7 deletions(-) create mode 100644 features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNavigator.kt diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts index e647ebb919..80ac4b3ba3 100644 --- a/features/preferences/impl/build.gradle.kts +++ b/features/preferences/impl/build.gradle.kts @@ -64,6 +64,7 @@ dependencies { implementation(projects.libraries.testtags) implementation(projects.libraries.uiStrings) implementation(projects.libraries.matrixui) + implementation(projects.libraries.mediaviewer.api) implementation(projects.libraries.mediapickers.api) implementation(projects.libraries.mediaupload.api) implementation(projects.libraries.permissions.api) @@ -108,6 +109,7 @@ dependencies { testImplementation(projects.libraries.push.test) testImplementation(projects.libraries.pushstore.test) testImplementation(projects.libraries.roomselect.test) + testImplementation(projects.libraries.mediaviewer.test) testImplementation(projects.libraries.troubleshoot.test) testImplementation(projects.features.deactivation.test) testImplementation(projects.features.enterprise.test) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt index 15718ca0f0..c85eed5b9d 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt @@ -40,10 +40,12 @@ import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.appyx.canPop import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.di.SessionScope 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.user.MatrixUser +import io.element.android.libraries.mediaviewer.api.FileViewerEntryPoint import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint import io.element.android.libraries.troubleshoot.api.PushHistoryEntryPoint import kotlinx.parcelize.Parcelize @@ -56,6 +58,7 @@ class PreferencesFlowNode( private val lockScreenEntryPoint: LockScreenEntryPoint, private val notificationTroubleShootEntryPoint: NotificationTroubleShootEntryPoint, private val pushHistoryEntryPoint: PushHistoryEntryPoint, + private val fileViewerEntryPoint: FileViewerEntryPoint, private val logoutEntryPoint: LogoutEntryPoint, private val openSourceLicensesEntryPoint: OpenSourceLicensesEntryPoint, private val accountDeactivationEntryPoint: AccountDeactivationEntryPoint, @@ -95,6 +98,9 @@ class PreferencesFlowNode( @Parcelize data object PushHistory : NavTarget + @Parcelize + data class PushRules(val filename: String, val content: String) : NavTarget + @Parcelize data object LockScreenSettings : NavTarget @@ -187,6 +193,10 @@ class PreferencesFlowNode( } NavTarget.DeveloperSettings -> { val developerSettingsCallback = object : DeveloperSettingsNode.Callback { + override fun navigateToPushRules(filename: String, content: String) { + backstack.push(NavTarget.PushRules(filename = filename, content = content)) + } + override fun navigateToPushHistory() { backstack.push(NavTarget.PushHistory) } @@ -270,6 +280,26 @@ class PreferencesFlowNode( }, ) } + is NavTarget.PushRules -> { + fileViewerEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = FileViewerEntryPoint.Params( + filename = navTarget.filename, + mimeType = MimeTypes.Json, + content = navTarget.content, + ), + callback = object : FileViewerEntryPoint.Callback { + override fun onDone() { + if (backstack.canPop()) { + backstack.pop() + } else { + navigateUp() + } + } + }, + ) + } is NavTarget.EditDefaultNotificationSetting -> { val callback = object : EditDefaultNotificationSettingNode.Callback { override fun navigateToRoomNotificationSettings(roomId: RoomId) { diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvent.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvent.kt index e0ad208fc1..34ccba2abe 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvent.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvent.kt @@ -17,4 +17,6 @@ sealed interface DeveloperSettingsEvent { data object VacuumStores : DeveloperSettingsEvent data class MarkAllRoomsAsRead(val needsConfirmation: Boolean) : DeveloperSettingsEvent data object DismissMarkAllRoomsAsReadConfirmation : DeveloperSettingsEvent + data object OpenPushRules : DeveloperSettingsEvent + data object DismissPushRulesError : DeveloperSettingsEvent } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNavigator.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNavigator.kt new file mode 100644 index 0000000000..8201c7a14d --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNavigator.kt @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.preferences.impl.developer + +/** + * Navigation performed by the developer settings screen, when data has to be loaded first. + */ +fun interface DeveloperSettingsNavigator { + fun openPushRules(filename: String, content: String) +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt index a8a8c63eed..7a8c3222d1 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt @@ -28,15 +28,22 @@ import io.element.android.libraries.di.SessionScope class DeveloperSettingsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, - private val presenter: DeveloperSettingsPresenter, + presenterFactory: DeveloperSettingsPresenter.Factory, private val extraDeveloperOptionsRenderer: ExtraDeveloperOptionsRenderer, -) : Node(buildContext, plugins = plugins) { +) : Node(buildContext, plugins = plugins), + DeveloperSettingsNavigator { interface Callback : Plugin { + fun navigateToPushRules(filename: String, content: String) fun navigateToPushHistory() fun onDone() } private val callback: Callback = callback() + private val presenter = presenterFactory.create(navigator = this) + + override fun openPushRules(filename: String, content: String) { + callback.navigateToPushRules(filename = filename, content = content) + } @Composable override fun View(modifier: Modifier) { diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt index c925433608..9b523d1c34 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt @@ -17,7 +17,9 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.graphics.toArgb -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.Assisted +import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.preferences.impl.developer.appsettings.AppDeveloperSettingsState import io.element.android.features.preferences.impl.tasks.ClearCacheUseCase @@ -34,13 +36,15 @@ import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.matrix.api.analytics.GetDatabaseSizesUseCase import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -@Inject +@AssistedInject class DeveloperSettingsPresenter( + @Assisted private val navigator: DeveloperSettingsNavigator, private val appDeveloperSettingsPresenter: Presenter, private val sessionId: SessionId, private val deviceId: DeviceId, @@ -52,7 +56,13 @@ class DeveloperSettingsPresenter( private val fileSizeFormatter: FileSizeFormatter, private val markAllRoomsAsRead: MarkAllRoomsAsRead, private val buildMeta: BuildMeta, + private val notificationSettingsService: NotificationSettingsService, ) : Presenter { + @AssistedFactory + fun interface Factory { + fun create(navigator: DeveloperSettingsNavigator): DeveloperSettingsPresenter + } + @Composable override fun present(): DeveloperSettingsState { val cacheSize = remember { @@ -67,6 +77,9 @@ class DeveloperSettingsPresenter( val markAllRoomsAsReadAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } + val pushRulesAction = remember { + mutableStateOf>(AsyncAction.Uninitialized) + } var showColorPicker by remember { mutableStateOf(false) } @@ -109,6 +122,10 @@ class DeveloperSettingsPresenter( DeveloperSettingsEvent.DismissMarkAllRoomsAsReadConfirmation -> { markAllRoomsAsReadAction.value = AsyncAction.Uninitialized } + DeveloperSettingsEvent.OpenPushRules -> coroutineScope.openPushRules(pushRulesAction) + DeveloperSettingsEvent.DismissPushRulesError -> { + pushRulesAction.value = AsyncAction.Uninitialized + } } } @@ -119,6 +136,7 @@ class DeveloperSettingsPresenter( databaseSizes = databaseSizes.value, clearCacheAction = clearCacheAction.value, markAllRoomsAsReadAction = markAllRoomsAsReadAction.value, + pushRulesAction = pushRulesAction.value, isEnterpriseBuild = buildMeta.isEnterpriseBuild, showColorPicker = showColorPicker, deviceId = deviceId, @@ -164,4 +182,25 @@ class DeveloperSettingsPresenter( markAllRoomsAsRead().getOrThrow() }.runCatchingUpdatingState(state = markAllRoomsAsReadAction) } + + private fun CoroutineScope.openPushRules(pushRulesAction: MutableState>) = launch { + pushRulesAction.value = AsyncAction.Loading + notificationSettingsService.getRawPushRules() + .onSuccess { content -> + pushRulesAction.value = AsyncAction.Uninitialized + navigator.openPushRules( + filename = pushRulesFilename(), + content = content.orEmpty(), + ) + } + .onFailure { + pushRulesAction.value = AsyncAction.Failure(it) + } + } + + /** + * The user id contains a colon, which is not a valid character for a file name on all the file systems, + * so replace it by an underscore. + */ + private fun pushRulesFilename() = "push_rules${sessionId.value.replace(':', '_')}.json" } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsState.kt index 3a859f14bb..a4345c6452 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsState.kt @@ -20,10 +20,13 @@ data class DeveloperSettingsState( val databaseSizes: AsyncData>, val clearCacheAction: AsyncAction, val markAllRoomsAsReadAction: AsyncAction, + val pushRulesAction: AsyncAction, val isEnterpriseBuild: Boolean, val showColorPicker: Boolean, val deviceId: DeviceId, val eventSink: (DeveloperSettingsEvent) -> Unit ) { - val showLoader = clearCacheAction is AsyncAction.Loading || markAllRoomsAsReadAction is AsyncAction.Loading + val showLoader = clearCacheAction is AsyncAction.Loading || + markAllRoomsAsReadAction is AsyncAction.Loading || + pushRulesAction is AsyncAction.Loading } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStatePreviewParam.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStatePreviewParam.kt index cbe95d6b20..0a42251b40 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStatePreviewParam.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStatePreviewParam.kt @@ -32,6 +32,9 @@ open class DeveloperSettingsStatePreviewParam : PreviewParameterProvider = AsyncAction.Uninitialized, markAllRoomsAsReadAction: AsyncAction = AsyncAction.Uninitialized, + pushRulesAction: AsyncAction = AsyncAction.Uninitialized, isEnterpriseBuild: Boolean = false, showColorPicker: Boolean = false, deviceId: DeviceId = DeviceId("ILAKNDNASDLK"), @@ -49,6 +53,7 @@ fun aDeveloperSettingsState( databaseSizes = AsyncData.Success(persistentMapOf("state_store" to "1.2MB")), clearCacheAction = clearCacheAction, markAllRoomsAsReadAction = markAllRoomsAsReadAction, + pushRulesAction = pushRulesAction, isEnterpriseBuild = isEnterpriseBuild, showColorPicker = showColorPicker, deviceId = deviceId, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt index e143f42b0b..8eff49c471 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt @@ -23,8 +23,10 @@ import io.element.android.compound.theme.ElementTheme import io.element.android.features.preferences.impl.R import io.element.android.features.preferences.impl.developer.appsettings.AppDeveloperSettingsView import io.element.android.libraries.androidutils.system.copyToClipboard +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog +import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory import io.element.android.libraries.designsystem.components.preferences.PreferencePage @@ -60,6 +62,12 @@ fun DeveloperSettingsView( onDismiss = { state.eventSink(DeveloperSettingsEvent.DismissMarkAllRoomsAsReadConfirmation) }, ) } + (state.pushRulesAction as? AsyncAction.Failure)?.let { failure -> + ErrorDialog( + content = failure.error.message ?: stringResource(CommonStrings.error_unknown), + onSubmit = { state.eventSink(DeveloperSettingsEvent.DismissPushRulesError) }, + ) + } BackHandler( enabled = !state.showLoader, onBack = onBackClick, @@ -79,7 +87,10 @@ fun DeveloperSettingsView( onOpenShowkase = onOpenShowkase, ) SessionCategory(deviceId = state.deviceId) - NotificationCategory(onPushHistoryClick) + NotificationCategory( + onPushRulesClick = { state.eventSink(DeveloperSettingsEvent.OpenPushRules) }, + onPushHistoryClick = onPushHistoryClick, + ) MarkAllRoomsAsReadCategory(state) if (state.isEnterpriseBuild) { @@ -217,8 +228,17 @@ private fun MarkAllRoomsAsReadCategory(state: DeveloperSettingsState) { } @Composable -private fun NotificationCategory(onPushHistoryClick: () -> Unit) { +private fun NotificationCategory( + onPushRulesClick: () -> Unit, + onPushHistoryClick: () -> Unit, +) { PreferenceCategory(title = stringResource(id = R.string.screen_notification_settings_title)) { + ListItem( + content = { + Text("Push rules") + }, + onClick = onPushRulesClick, + ) ListItem( content = { Text(stringResource(R.string.troubleshoot_notifications_entry_point_push_history_title)) diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/DefaultPreferencesEntryPointTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/DefaultPreferencesEntryPointTest.kt index 963d7846b4..afc3801c88 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/DefaultPreferencesEntryPointTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/DefaultPreferencesEntryPointTest.kt @@ -19,6 +19,7 @@ import io.element.android.features.logout.test.FakeLogoutEntryPoint import io.element.android.features.preferences.api.PreferencesEntryPoint import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.mediaviewer.test.FakeFileViewerEntryPoint import io.element.android.libraries.troubleshoot.test.FakeNotificationTroubleShootEntryPoint import io.element.android.libraries.troubleshoot.test.FakePushHistoryEntryPoint import io.element.android.tests.testutils.lambda.lambdaError @@ -43,6 +44,7 @@ class DefaultPreferencesEntryPointTest { lockScreenEntryPoint = FakeLockScreenEntryPoint(), notificationTroubleShootEntryPoint = FakeNotificationTroubleShootEntryPoint(), pushHistoryEntryPoint = FakePushHistoryEntryPoint(), + fileViewerEntryPoint = FakeFileViewerEntryPoint(), logoutEntryPoint = FakeLogoutEntryPoint(), openSourceLicensesEntryPoint = FakeOpenSourceLicensesEntryPoint(), accountDeactivationEntryPoint = FakeAccountDeactivationEntryPoint(), 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 753d89da95..66f96e5eb3 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 @@ -28,10 +28,13 @@ import io.element.android.libraries.matrix.api.analytics.GetDatabaseSizesUseCase import io.element.android.libraries.matrix.api.analytics.SdkStoreSizes import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.test.A_DEVICE_ID import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.core.aBuildMeta +import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.test @@ -41,6 +44,9 @@ import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test +private const val A_PUSH_RULES_CONTENT = "{\"global\":{}}" +private val AN_EXCEPTION = Exception("A failure") + class DeveloperSettingsPresenterTest { @get:Rule val warmUpRule = WarmUpRule() @@ -170,7 +176,53 @@ class DeveloperSettingsPresenterTest { } } + @Test + fun `present - OpenPushRules event navigates to the push rules content`() = runTest { + val openPushRulesLambda = lambdaRecorder { _, _ -> } + val presenter = createDeveloperSettingsPresenter( + navigator = DeveloperSettingsNavigator(openPushRulesLambda), + notificationSettingsService = FakeNotificationSettingsService( + getRawPushRulesResult = { Result.success(A_PUSH_RULES_CONTENT) }, + ), + ) + presenter.test { + val state = awaitItem() + assertThat(state.pushRulesAction).isEqualTo(AsyncAction.Uninitialized) + state.eventSink(DeveloperSettingsEvent.OpenPushRules) + skipItems(1) + awaitItem().also { + assertThat(it.pushRulesAction).isEqualTo(AsyncAction.Uninitialized) + } + openPushRulesLambda.assertions().isCalledOnce().with( + value("push_rules@alice_server.org.json"), + value(A_PUSH_RULES_CONTENT), + ) + } + } + + @Test + fun `present - OpenPushRules event failure can be dismissed`() = runTest { + val presenter = createDeveloperSettingsPresenter( + notificationSettingsService = FakeNotificationSettingsService( + getRawPushRulesResult = { Result.failure(AN_EXCEPTION) }, + ), + ) + presenter.test { + val state = awaitItem() + state.eventSink(DeveloperSettingsEvent.OpenPushRules) + skipItems(1) + awaitItem().also { + assertThat(it.pushRulesAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) + it.eventSink(DeveloperSettingsEvent.DismissPushRulesError) + } + awaitItem().also { + assertThat(it.pushRulesAction).isEqualTo(AsyncAction.Uninitialized) + } + } + } + private fun createDeveloperSettingsPresenter( + navigator: DeveloperSettingsNavigator = DeveloperSettingsNavigator { _, _ -> lambdaError() }, sessionId: SessionId = A_SESSION_ID, deviceId: DeviceId = A_DEVICE_ID, cacheSizeUseCase: FakeComputeCacheSizeUseCase = FakeComputeCacheSizeUseCase(), @@ -180,8 +232,10 @@ class DeveloperSettingsPresenterTest { databaseSizesUseCase: GetDatabaseSizesUseCase = GetDatabaseSizesUseCase { Result.success(SdkStoreSizes(null, null, null, null)) }, markAllRoomsAsRead: FakeMarkAllRoomsAsRead = FakeMarkAllRoomsAsRead(), buildMeta: BuildMeta = aBuildMeta(), + notificationSettingsService: NotificationSettingsService = FakeNotificationSettingsService(), ): DeveloperSettingsPresenter { return DeveloperSettingsPresenter( + navigator = navigator, appDeveloperSettingsPresenter = { anAppDeveloperSettingsState() }, sessionId = sessionId, deviceId = deviceId, @@ -193,6 +247,7 @@ class DeveloperSettingsPresenterTest { fileSizeFormatter = FakeFileSizeFormatter(), markAllRoomsAsRead = markAllRoomsAsRead, buildMeta = buildMeta, + notificationSettingsService = notificationSettingsService, ) } } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsViewTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsViewTest.kt index f1151bd3e1..fcc69cda91 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsViewTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsViewTest.kt @@ -57,6 +57,19 @@ class DeveloperSettingsViewTest : RobolectricTest() { } } + @Config(qualifiers = "h2400dp") + @Test + fun `clicking on push rules emits the expected event`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() + setDeveloperSettingsView( + state = aDeveloperSettingsState( + eventSink = eventsRecorder + ), + ) + onNodeWithText("Push rules").performScrollTo().performClick() + eventsRecorder.assertSingle(DeveloperSettingsEvent.OpenPushRules) + } + @Config(qualifiers = "h2000dp") @Test fun `clicking on open showkase invokes the expected callback`() = runAndroidComposeUiTest { From 2fcf806f59de756c10e4b4a02666a9f53b1086a1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Aug 2026 12:55:07 +0200 Subject: [PATCH 095/112] Pretty print the push rules content before displaying it The raw push rules returned by the SDK are a single long line of json, which is hard to read on a phone. Re-encode the content with a pretty printer using a two space indentation before handing it to the file viewer. Parsing to a JsonElement and re-encoding preserves the order of the keys, so the rules are still listed in the order the server returned them. Content which cannot be parsed is passed through unchanged, so that a malformed payload remains viewable. Co-Authored-By: Claude Opus 5 (1M context) --- features/preferences/impl/build.gradle.kts | 1 + .../developer/DeveloperSettingsPresenter.kt | 21 +++++++++++- .../DeveloperSettingsPresenterTest.kt | 33 +++++++++++++++++-- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts index 80ac4b3ba3..12554eb6fa 100644 --- a/features/preferences/impl/build.gradle.kts +++ b/features/preferences/impl/build.gradle.kts @@ -92,6 +92,7 @@ dependencies { implementation(libs.color.picker) implementation(libs.androidx.browser) implementation(libs.androidx.datastore.preferences) + implementation(libs.serialization.json) api(projects.features.preferences.api) implementation(libs.showkase) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt index 9b523d1c34..74bf595ce4 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt @@ -32,6 +32,7 @@ import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.core.data.ByteUnit +import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.matrix.api.analytics.GetDatabaseSizesUseCase import io.element.android.libraries.matrix.api.core.DeviceId @@ -41,6 +42,9 @@ import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement @AssistedInject class DeveloperSettingsPresenter( @@ -190,7 +194,7 @@ class DeveloperSettingsPresenter( pushRulesAction.value = AsyncAction.Uninitialized navigator.openPushRules( filename = pushRulesFilename(), - content = content.orEmpty(), + content = content.orEmpty().prettyPrintJson(), ) } .onFailure { @@ -204,3 +208,18 @@ class DeveloperSettingsPresenter( */ private fun pushRulesFilename() = "push_rules${sessionId.value.replace(':', '_')}.json" } + +@OptIn(ExperimentalSerializationApi::class) +private val prettyPrintJson = Json { + prettyPrint = true + // Keep the indentation small, for a better rendering on mobile. + prettyPrintIndent = " " +} + +/** + * Pretty print this json content, so that it is readable when rendered. + * Return the content as is if it is not valid json. + */ +private fun String.prettyPrintJson(): String = runCatchingExceptions { + prettyPrintJson.encodeToString(JsonElement.serializer(), prettyPrintJson.parseToJsonElement(this)) +}.getOrDefault(this) 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 66f96e5eb3..b76939f72c 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 @@ -44,7 +44,17 @@ import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test -private const val A_PUSH_RULES_CONTENT = "{\"global\":{}}" +private const val A_PUSH_RULES_CONTENT = """{"global":{"override":[{"rule_id":".m.rule.master","enabled":false}]}}""" +private const val A_PUSH_RULES_CONTENT_PRETTY_PRINTED = """{ + "global": { + "override": [ + { + "rule_id": ".m.rule.master", + "enabled": false + } + ] + } +}""" private val AN_EXCEPTION = Exception("A failure") class DeveloperSettingsPresenterTest { @@ -195,7 +205,26 @@ class DeveloperSettingsPresenterTest { } openPushRulesLambda.assertions().isCalledOnce().with( value("push_rules@alice_server.org.json"), - value(A_PUSH_RULES_CONTENT), + value(A_PUSH_RULES_CONTENT_PRETTY_PRINTED), + ) + } + } + + @Test + fun `present - OpenPushRules event keeps the content as is when it is not valid json`() = runTest { + val openPushRulesLambda = lambdaRecorder { _, _ -> } + val presenter = createDeveloperSettingsPresenter( + navigator = DeveloperSettingsNavigator(openPushRulesLambda), + notificationSettingsService = FakeNotificationSettingsService( + getRawPushRulesResult = { Result.success("not json") }, + ), + ) + presenter.test { + awaitItem().eventSink(DeveloperSettingsEvent.OpenPushRules) + skipItems(2) + openPushRulesLambda.assertions().isCalledOnce().with( + value("push_rules@alice_server.org.json"), + value("not json"), ) } } From 0e93a943a5bfa9f8778d8d0231275bb723912bbc Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 26 Aug 2026 11:16:07 +0000 Subject: [PATCH 096/112] Update screenshots --- ...ferences.impl.developer_DeveloperSettingsView_Day_3_en.png | 3 +++ ...rences.impl.developer_DeveloperSettingsView_Night_3_en.png | 3 +++ ...es.preferences.impl.userstatus_UserStatusView_Day_5_en.png | 4 ++-- ...es.preferences.impl.userstatus_UserStatusView_Day_6_en.png | 4 ++-- ....preferences.impl.userstatus_UserStatusView_Night_5_en.png | 4 ++-- ....preferences.impl.userstatus_UserStatusView_Night_6_en.png | 4 ++-- ...embermoderation.impl_RoomMemberModerationView_Day_5_en.png | 4 ++-- ...embermoderation.impl_RoomMemberModerationView_Day_7_en.png | 4 ++-- ...bermoderation.impl_RoomMemberModerationView_Night_5_en.png | 4 ++-- ...bermoderation.impl_RoomMemberModerationView_Night_7_en.png | 4 ++-- ...eme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png | 4 ++-- ...e.components_HorizontalFloatingToolbarNoFab_Night_0_en.png | 4 ++-- ...em.theme.components_HorizontalFloatingToolbar_Day_0_en.png | 4 ++-- ....theme.components_HorizontalFloatingToolbar_Night_0_en.png | 4 ++-- ...es.mediaviewer.impl.fileviewer_FileViewerView_Day_0_en.png | 3 +++ ...es.mediaviewer.impl.fileviewer_FileViewerView_Day_1_en.png | 3 +++ ...es.mediaviewer.impl.fileviewer_FileViewerView_Day_2_en.png | 3 +++ ....mediaviewer.impl.fileviewer_FileViewerView_Night_0_en.png | 3 +++ ....mediaviewer.impl.fileviewer_FileViewerView_Night_1_en.png | 3 +++ ....mediaviewer.impl.fileviewer_FileViewerView_Night_2_en.png | 3 +++ 20 files changed, 48 insertions(+), 24 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_3_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_3_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Day_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Day_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Night_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Night_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Night_2_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_3_en.png new file mode 100644 index 0000000000..da05331f32 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a5a8e393b6c77066e69b25e41a82850f86d0b8b283cecc7f7c8416d612974d7 +size 43097 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_3_en.png new file mode 100644 index 0000000000..0fe722a849 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81cffb805bfeda8d0ce58f6c54fb298cc2b35d0ce210f514fd7ccb2fa23ab82f +size 40978 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_5_en.png index 2c15fd6a37..82f344f714 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7260abe16fc2ab4f6ada97ea0f756dc5c9908ff3b61d6ec1723ffde302bb3dc5 -size 11955 +oid sha256:8d96eefa01a5de803b0cc9d9109c20df86eaff01b73ff24a91945b43d53d5d01 +size 11960 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_6_en.png index 7c422591ad..fdd8fcd5a5 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e16fc1daa0f284c442f66591e1d1f59161272a67998d26740b0b403bfe82495 -size 11866 +oid sha256:22678a2a8505e1d9d5bfcb44459b6fac69c11a4bf72fae09c48ffa2e54f5a110 +size 12128 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_5_en.png index acb8afa74a..f6e27475da 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bed7565420e11c43acc06896ef298683d2f2dd1dc16c430eab7018e9ead9937a -size 11652 +oid sha256:9ee89826c8aa2ac0371ed31959243f62bf9968806a7b27117cd7b4d849423a33 +size 11667 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_6_en.png index e0a4cb1c97..b1bb69efb8 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:87811df502c760816e74beb006341627857c39380ce715f1a747ac9fce77b226 -size 11516 +oid sha256:e27cb9e501ccbac54926f0252b4098867643a2d2683c5b267f86ad99edf6f5d7 +size 11817 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png index 5c597300e4..73b8b8e50c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21aee17ee9a292bb2f0d3d2ec2db565ebb8d5df753454a5a002f033ed5ed39c5 -size 3853 +oid sha256:e8a3ad99763929d2783147814e44fa9ec7e7efb7b99949e3580f23c38fb5d971 +size 9398 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en.png index f8a1f24b34..5c597300e4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1342b6427f5b2e3075ae3ccee60bf1d3b11afaccf3e152643d6ef2d65c21b54e -size 9108 +oid sha256:21aee17ee9a292bb2f0d3d2ec2db565ebb8d5df753454a5a002f033ed5ed39c5 +size 3853 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en.png index 4ae7279fe7..d47bf90664 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:410a8596d4c70b9a3856d959d6d71ade8e831e8581ff410c9900957a93b3eb50 -size 8114 +oid sha256:520cae2544153ba45f8fe4b021286c2d22da06aa1b60be7f1b13879723ff994c +size 3664 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png index d47bf90664..a87d89c701 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:520cae2544153ba45f8fe4b021286c2d22da06aa1b60be7f1b13879723ff994c -size 3664 +oid sha256:0f3a0cb5d9bae6725a1c527f3d9575aabf85d3edf63a28d9faa23f4f0b65e2d0 +size 7624 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png index ee5c582240..58546b8522 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:75de341f705bb22955f9399180519b65ad59c2e1917317d56e50ea4db78f35bc -size 12102 +oid sha256:30357bf18792db5ab20085010251170a724ffe8c00313facd04ee704e806af64 +size 10982 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png index 52fd9b9693..fedead0fa8 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58b9491aaa5554b89d86c62a5c9c44975367538c6baa5562f8dc5768f76d8264 -size 8705 +oid sha256:4811667dec0f32accc5e2c8a5dd5c0b34017fcff8d1e0376ee7a4708426bca0f +size 9838 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en.png index 38ad0e4b56..83302d6ba4 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:326d59cf768b2dbd96eb7964d54c8eda509a5cd57c6e62087625e3314da0bb4d -size 13658 +oid sha256:16dee1dfe746cd40dac9fe8f0af3ecb42cd14bab5cc490917b589beed065ed1b +size 14686 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en.png index 687e0d436a..4da2a98aca 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d57df7110371c9224e232b707d6a1b10ce14afcb56c803a78220792f2a0d4cf2 -size 11354 +oid sha256:ef0dc4d53daeb986f378b6e4d85928b4b6729336c89cf21e642c88c53688a3ab +size 10292 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Day_0_en.png new file mode 100644 index 0000000000..3cec94cb44 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1bfd26e506a4e1693d0d0720add60ab9aa2b01558e0d14c8294bedd5b42ca2fb +size 15005 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Day_1_en.png new file mode 100644 index 0000000000..5d370f30d9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5103803369870f9cd661f1b5cbbb70023cef586276fca80f010432596b8ecbc3 +size 15026 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Day_2_en.png new file mode 100644 index 0000000000..a62a416966 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec16ff6ed3abbd4a04cf8d4b0664424e109736013868e790496afe5c7b106646 +size 14861 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Night_0_en.png new file mode 100644 index 0000000000..8fe23cb9be --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e7351236d535315198d7d6d8d63d36deb26360af1ff3ed6f148e7c811ee69056 +size 14852 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Night_1_en.png new file mode 100644 index 0000000000..d679329fb8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5bff0dd25d4ea06c23bc2fa8968892e8b07da65dfba57a61788d2955187b7c9c +size 14845 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Night_2_en.png new file mode 100644 index 0000000000..ac90f5b949 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.fileviewer_FileViewerView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ffc62fd0e9b765cfe43193cf0dd443fcf7199330f4a3f9691f9627e423c3f64 +size 14774 From 267e48aa697bd8c5c280cffcf3ea76287ea77437 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Aug 2026 13:48:11 +0200 Subject: [PATCH 097/112] Add instructions to let the CI record the screenshot if necessary. Also update instruction about PR size. --- AGENTS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 917285ab30..a4a73ca965 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -50,9 +50,10 @@ PRs must meet these rules. - Title = changelog entry — descriptive, no "Fixes #…". - Leave description template for the developer. Redirect them to the [contributing etiquette](CONTRIBUTING.md#etiquette). - Screenshots/videos for visual changes. -- 500 additions max — split large changes. +- 500 additions max into production code. Test code can be larger — split large changes. - Commits need a title and description; no tiny or massive commits. - No history rewrites. +- When creating a pull request and if changes contain new or update(s) of the Composable Previews, add the label "Record-Screenshots" to the PR so that the CI will record the screenshots. --- From 94259962a07517722ce8e7ad47b1fc9d6526324a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Aug 2026 13:56:12 +0200 Subject: [PATCH 098/112] Setting version for the release 26.08.3 --- plugins/src/main/kotlin/Versions.kt | 2 +- tools/manifest/gplay/release/aaptDump.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 89ad4a63fc..3e4b4f5c7f 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -45,7 +45,7 @@ private const val versionMonth = 8 * Release number in the month. Value must be in [0,99]. * Do not update this value. it is updated by the release script. */ -private const val versionReleaseNumber = 2 +private const val versionReleaseNumber = 3 object Versions { /** diff --git a/tools/manifest/gplay/release/aaptDump.txt b/tools/manifest/gplay/release/aaptDump.txt index 7eae1a5a78..457a6ea1cc 100644 --- a/tools/manifest/gplay/release/aaptDump.txt +++ b/tools/manifest/gplay/release/aaptDump.txt @@ -1,4 +1,4 @@ -package: name='io.element.android.x' versionCode='202608020' versionName='26.08.2' platformBuildVersionName='17' platformBuildVersionCode='37' compileSdkVersion='37' compileSdkVersionCodename='17' +package: name='io.element.android.x' versionCode='202608030' versionName='26.08.3' platformBuildVersionName='17' platformBuildVersionCode='37' compileSdkVersion='37' compileSdkVersionCodename='17' sdkVersion:'24' targetSdkVersion:'37' uses-permission: name='android.permission.POST_NOTIFICATIONS' From cbc7de9982800b3da92992a965857be28f8dceae Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Aug 2026 13:56:41 +0200 Subject: [PATCH 099/112] Adding fastlane file for version 26.08.3 --- fastlane/metadata/android/en-US/changelogs/202608030.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/202608030.txt diff --git a/fastlane/metadata/android/en-US/changelogs/202608030.txt b/fastlane/metadata/android/en-US/changelogs/202608030.txt new file mode 100644 index 0000000000..a4b397f1bb --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/202608030.txt @@ -0,0 +1,2 @@ +Main changes in this version: bug fixes and improvements. +Full changelog: https://github.com/element-hq/element-x-android/releases \ No newline at end of file From 0e86de4e584811d5ea9755d52184cfd4e466e530 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Aug 2026 15:35:00 +0200 Subject: [PATCH 100/112] Changelog for version 26.08.3 --- CHANGES.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index db18e59251..a56dd4d1bb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,67 @@ +Changes in Element X v26.08.3 +============================= + + + +## What's Changed +### ✨ Features +* Let users enter their full Matrix user ID on the account provider screen by @jennaharris7 in https://github.com/element-hq/element-x-android/pull/7500 +* Enable user status feature and hide status text in the timeline by @bmarty in https://github.com/element-hq/element-x-android/pull/7517 +### 🐛 Bugfixes +* Fix cold start with a mobile deep link by @bmarty in https://github.com/element-hq/element-x-android/pull/7488 +* Make the account provider screen scrollable so its description is not clipped by the keyboard by @jennaharris7 in https://github.com/element-hq/element-x-android/pull/7521 +### 🧱 Build +* Update Gradle Wrapper from 9.7.0 to 9.7.1 by @ElementBot in https://github.com/element-hq/element-x-android/pull/7485 +* Fix flaky test in `DefaultVoiceMessageComposerPresenterTest` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7487 +* Fix warning about deprecated Gradle `keepRules.file` API by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7498 +* Update Enterprise Copyright in idea settings, so that new files get an expected header. by @bmarty in https://github.com/element-hq/element-x-android/pull/7501 +* Remove workaround for Renovate. by @bmarty in https://github.com/element-hq/element-x-android/pull/7502 +### 🚧 In development 🚧 +* Make PIP react to camera orientation changes in the other participant's device by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6951 +* Feature: global search by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7410 +### Dependency upgrades +* Update dependency org.maplibre.gl:android-sdk to v13.5.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7486 +* Update dependency androidx.webkit:webkit to v1.17.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7484 +* Update dependency io.sentry:sentry-android to v8.53.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7481 +* Update dependency io.element.android:element-call-embedded to v0.24.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7474 +* Update roborazzi to v1.72.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7489 +* Update metro to v1.4.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7493 +* Update dependency com.squareup.okhttp3:okhttp-bom to v5.5.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7512 +* Update dependency com.google.guava:guava to v33.7.0-android by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7516 +* Update sdk to version 26.08.25 by @bmarty in https://github.com/element-hq/element-x-android/pull/7550 +### 💼 Element Pro +* Add `ClientEnterpriseHook` and `ClientBuilderEnterpriseHook` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7495 +* Remove dependency on enterprise module. by @bmarty in https://github.com/element-hq/element-x-android/pull/7505 +### Others +* Show the right image when opening one that has not finished sending by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7359 +* Move strings to the login module by @bmarty in https://github.com/element-hq/element-x-android/pull/7494 +* Allow empty problem description if the GitHub issue number is present for rageshakes by @bxdxnn in https://github.com/element-hq/element-x-android/pull/7497 +* Apply the system theme when switching back to it from a forced light or dark theme by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7453 +* Show dialog with reason when user clicks on a message with a failure by @bmarty in https://github.com/element-hq/element-x-android/pull/7490 +* Keep a focused timeline focused until the event has been rendered by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7463 +* Do not render in-chat key verification requests in the timeline by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7443 +* Share a file whose mime type is not one of the supported ones by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7435 +* Show reactions in the pinned messages list by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6676 +* Send a concrete mime type for media shared with a wildcard one by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7449 +* Rename module and ensure that naming convention is followed by @bmarty in https://github.com/element-hq/element-x-android/pull/7514 +* Fix lint issue, ensure that PiP methods are not called on old devices not supporting them. by @bmarty in https://github.com/element-hq/element-x-android/pull/7515 +* Rename PreviewParameterProvider subclasses to the PreviewParam suffix by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7468 +* Update Ref. by @bmarty in https://github.com/element-hq/element-x-android/pull/7518 +* Remove the reference to the decommissioned OIDC playground by @sandhose in https://github.com/element-hq/element-x-android/pull/7519 +* Hide the encryption badge when key storage is set up on the server by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7447 +* Rename remaining events sealed interface by @bmarty in https://github.com/element-hq/element-x-android/pull/7533 +* Stop waiting on a verification request the other session never accepts by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7457 +* Point the proxy debugging doc at the method that builds the clients by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7543 +* Show the build date in the screenshot gallery footer by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7544 +* Run the database migrations again when a database is upgraded by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7570 +* Update fonts on the headings by @bmarty in https://github.com/element-hq/element-x-android/pull/7520 +* Add a "Push rules" entry in the developer options by @bmarty in https://github.com/element-hq/element-x-android/pull/7577 + +## New Contributors +* @sandhose made their first contribution in https://github.com/element-hq/element-x-android/pull/7519 + +**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.08.2...v26.08.3 + Changes in Element X v26.08.2 ============================= From c5f70de9043831d15e595802864f02b3e5f05883 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Aug 2026 17:26:23 +0200 Subject: [PATCH 101/112] Add releaseV2.sh, a release script automating the GitHub release releaseV2.sh is a copy of release.sh in which the GitHub CLI is mandatory and does what was previously done by hand: it finds and waits for the release.yml run, downloads and extracts its artifacts, creates the pre-release with its 6 assets and the generated release notes, and prepends those notes to CHANGES.md. It also compares the version code and version name of the signed artifacts to the expected values instead of asking the user to check them, and no longer needs ELEMENT_GITHUB_TOKEN, since the CLI uses its own credentials. release.sh is left untouched, so that the next release can be done with releaseV2.sh while keeping a way back if something goes wrong. The installation documentation is also fixed, as it referred to an asset name that does not exist anymore. Co-Authored-By: Claude Opus 5 (1M context) --- docs/install_from_github_release.md | 6 +- tools/release/releaseV2.sh | 541 ++++++++++++++++++++++++++++ 2 files changed, 544 insertions(+), 3 deletions(-) create mode 100755 tools/release/releaseV2.sh diff --git a/docs/install_from_github_release.md b/docs/install_from_github_release.md index 5ac27ff812..1fd963e953 100644 --- a/docs/install_from_github_release.md +++ b/docs/install_from_github_release.md @@ -64,16 +64,16 @@ brew install bundletool ### Steps 1. Open the GitHub release that you want to install from https://github.com/element-hq/element-x-android/releases -2. Download the asset `app-release-signed.aab` +2. Download the asset `app-gplay-release-signed.aab` 3. Navigate to the folder where you cloned the project and run the following command: ```bash -bundletool build-apks --bundle= --output=./tmp/elementx.apks \ +bundletool build-apks --bundle= --output=./tmp/elementx.apks \ --ks=./app/signature/debug.keystore --ks-pass=pass:android --ks-key-alias=androiddebugkey --key-pass=pass:android \ --overwrite ``` For instance: ```bash -bundletool build-apks --bundle=./tmp/Element/0.1.5/app-release-signed.aab --output=./tmp/elementx.apks \ +bundletool build-apks --bundle=./tmp/Element/26.08.3/app-gplay-release-signed.aab --output=./tmp/elementx.apks \ --ks=./app/signature/debug.keystore --ks-pass=pass:android --ks-key-alias=androiddebugkey --key-pass=pass:android \ --overwrite ``` diff --git a/tools/release/releaseV2.sh b/tools/release/releaseV2.sh new file mode 100755 index 0000000000..071d416d00 --- /dev/null +++ b/tools/release/releaseV2.sh @@ -0,0 +1,541 @@ +#!/usr/bin/env bash + +# Copyright (c) 2026 Element Creations Ltd. +# +# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. +# Please see LICENSE files in the repository root for full details. + +# do not exit when any command fails (issue with git flow) +set +e + +printf "\n================================================================================\n" +printf "| Welcome to the release script (V2)! |\n" +printf "================================================================================\n" + +printf "Checking environment...\n" +envError=0 + +# Check that bundletool is installed +if ! command -v bundletool &> /dev/null +then + printf "Fatal: bundletool is not installed. You can install it running \`brew install bundletool\`\n" + envError=1 +fi + +# Check that the GitHub CLI is installed. It is expected to be already authenticated, see `gh auth login`. +if ! command -v gh &> /dev/null +then + printf "Fatal: gh (the GitHub CLI) is not installed. You can install it running \`brew install gh\`\n" + envError=1 +fi + +# Path of the key store (it's a file) +keyStorePath="${ELEMENT_X_KEYSTORE_PATH}" +if [[ -z "${keyStorePath}" ]]; then + printf "Fatal: ELEMENT_X_KEYSTORE_PATH is not defined in the environment.\n" + envError=1 +fi +# Keystore password +keyStorePassword="${ELEMENT_X_KEYSTORE_PASSWORD}" +if [[ -z "${keyStorePassword}" ]]; then + printf "Fatal: ELEMENT_X_KEYSTORE_PASSWORD is not defined in the environment.\n" + envError=1 +fi +# Key password +keyPassword="${ELEMENT_X_KEY_PASSWORD}" +if [[ -z "${keyPassword}" ]]; then + printf "Fatal: ELEMENT_X_KEY_PASSWORD is not defined in the environment.\n" + envError=1 +fi +# Android home +androidHome="${ANDROID_HOME}" +if [[ -z "${androidHome}" ]]; then + printf "Fatal: ANDROID_HOME is not defined in the environment.\n" + envError=1 +fi +# @elementbot:matrix.org matrix token / Not mandatory +elementBotToken="${ELEMENT_BOT_MATRIX_TOKEN}" +if [[ -z "${elementBotToken}" ]]; then + printf "Warning: ELEMENT_BOT_MATRIX_TOKEN is not defined in the environment.\n" +fi + +if [ ${envError} == 1 ]; then + exit 1 +fi + +# Handle the result of a version check. ${checkError} must be set to 1 when a check has failed. +checkVersionResult() { + if [[ ${checkError} -ne 0 ]]; then + printf "\nThe check above has failed, this is not expected.\n" + read -r -p "Do you want to continue anyway (yes/no) default to no? " doContinue + doContinue=${doContinue:-no} + if [ "${doContinue}" != "yes" ]; then + exit 1 + fi + else + printf "The versions are correct.\n" + fi +} + +# Read minSdkVersion from file plugins/src/main/kotlin/Versions.kt +minSdkVersion=$(grep "MIN_SDK_FOSS =" ./plugins/src/main/kotlin/Versions.kt |cut -d '=' -f 2 |xargs) +# Read buildToolsVersion from file plugins/src/main/kotlin/Versions.kt +buildToolsVersion=$(grep "BUILD_TOOLS_VERSION =" ./plugins/src/main/kotlin/Versions.kt |cut -d '=' -f 2 |xargs) +buildToolsPath="${androidHome}/build-tools/${buildToolsVersion}" + +if [[ ! -d ${buildToolsPath} ]]; then + printf "Fatal: %s folder not found, ensure that you have installed the SDK version %s.\n" "${buildToolsPath}" "${buildToolsVersion}" + exit 1 +fi + +# Check if git flow is enabled +gitFlowDevelop=$(git config gitflow.branch.develop) +if [[ ${gitFlowDevelop} != "" ]] +then + printf "Git flow is initialized\n" +else + printf "Git flow is not initialized. Initializing...\n" + ./tools/gitflow/gitflow-init.sh +fi + +printf "OK\n" + +printf "\n================================================================================\n" +printf "Ensuring main and develop branches are up to date...\n" + +git checkout main +git pull +git checkout develop +git pull + +printf "\n================================================================================\n" +# Guessing version to propose a default version +versionsFile="./plugins/src/main/kotlin/Versions.kt" +# The version of the release must match the date of next monday, where the release is supposed to go live +# The command below gets the date of next monday +nextMondayDateCommand="date -v +1w -v -monday" +# Get release year on 2 digits +versionYearCandidate=$(${nextMondayDateCommand} +%y) +currentVersionMonth=$(grep "val versionMonth" ${versionsFile} | cut -d " " -f6) +# Get release month on 2 digits +versionMonthCandidate=$(${nextMondayDateCommand} +%m) +versionMonthCandidateNoLeadingZero=${versionMonthCandidate/#0/} +currentVersionReleaseNumber=$(grep "val versionReleaseNumber" ${versionsFile} | cut -d " " -f6) +# if the release month is the same as the current version, we increment the release number, else we reset it to 0 +if [[ ${currentVersionMonth} -eq ${versionMonthCandidateNoLeadingZero} ]]; then + versionReleaseNumberCandidate=$((currentVersionReleaseNumber + 1)) +else + versionReleaseNumberCandidate=0 +fi +versionCandidate="${versionYearCandidate}.${versionMonthCandidate}.${versionReleaseNumberCandidate}" + +read -r -p "Please enter the release version (example: ${versionCandidate}). Format must be 'YY.MM.x' or 'YY.MM.xy', with year and month matching next Monday. Just press enter if ${versionCandidate} is correct. " version +version=${version:-${versionCandidate}} + +# extract year, month and release number for future use +versionYear=$(echo "${version}" | cut -d "." -f1) +versionMonth=$(echo "${version}" | cut -d "." -f2) +versionMonthNoLeadingZero=${versionMonth/#0/} +versionReleaseNumber=$(echo "${version}" | cut -d "." -f3) + +printf "\n================================================================================\n" +printf "Starting the release %s\n" "${version}" +git flow release start "${version}" + +# Note: in case the release is already started and the script is started again, checkout the release branch again. +ret=$? +if [[ $ret -ne 0 ]]; then + printf "Mmh, it seems that the release is already started. I'm displaying the changes now:\n" + git diff --stat "release/${version}" origin/main + printf "Do you want to continue the release using its contents?\n\n" + read -r -p "Continue (yes/no) default to yes? " doContinue + doContinue=${doContinue:-yes} + if [ "${doContinue}" == "no" ]; then + printf "OK, exiting, you can start the release again with the command 'git flow release start %s'\n" "${version}" + exit 1 + fi + git checkout "release/${version}" +fi + +# Ensure version is OK +versionsFileBak="${versionsFile}.bak" +cp ${versionsFile} ${versionsFileBak} +sed "s/private const val versionYear = .*/private const val versionYear = ${versionYear}/" ${versionsFileBak} > ${versionsFile} +sed "s/private const val versionMonth = .*/private const val versionMonth = ${versionMonthNoLeadingZero}/" ${versionsFile} > ${versionsFileBak} +sed "s/private const val versionReleaseNumber = .*/private const val versionReleaseNumber = ${versionReleaseNumber}/" ${versionsFileBak} > ${versionsFile} +rm ${versionsFileBak} + +printf -v versionReleaseNumber2Digits "%02d" "${versionReleaseNumber}" +versionCode="20${versionYear}${versionMonth}${versionReleaseNumber2Digits}0" + +# Update the file aaptDump.txt with the new version +aaptDumpFile="./tools/manifest/gplay/release/aaptDump.txt" +sed "s/versionCode='[0-9]*'/versionCode='${versionCode}'/" ${aaptDumpFile} > ${aaptDumpFile}.bak +sed "s/versionName='[0-9]*\.[0-9]*\.[0-9]*'/versionName='${version}'/" ${aaptDumpFile}.bak > ${aaptDumpFile} +rm ${aaptDumpFile}.bak + +git commit -a -m "Setting version for the release ${version}" + +printf "\n================================================================================\n" +printf "Creating fastlane file...\n" +fastlaneFile="${versionCode}.txt" +fastlanePathFile="./fastlane/metadata/android/en-US/changelogs/${fastlaneFile}" +printf "Main changes in this version: bug fixes and improvements.\nFull changelog: https://github.com/element-hq/element-x-android/releases" > "${fastlanePathFile}" + +read -r -p "I have created the file ${fastlanePathFile}, please edit it and press enter to continue. " +git add "${fastlanePathFile}" +git commit -a -m "Adding fastlane file for version ${version}" + +printf "\n================================================================================\n" +printf "OK, finishing the release...\n" +git flow release finish "${version}" + +printf "\n================================================================================\n" +read -r -p "Done, push the branch 'main' and the new tag (yes/no) default to yes? " doPush +doPush=${doPush:-yes} + +if [ "${doPush}" == "yes" ]; then + printf "Pushing branch 'main' and tag 'v%s'...\n" "${version}" + git push origin main + git push origin "v${version}" +else + printf "Not pushing, do not forget to push manually!\n" +fi + +printf "\n================================================================================\n" +printf "Checking out develop...\n" +git checkout develop + +printf "\n================================================================================\n" +printf "Downloading the artifacts...\n" + +targetPath="./tmp/Element/${version}" +fdroidTargetPath="${targetPath}/fdroid" +gplayTargetPath="${targetPath}/gplay" + +releaseCommit=$(git rev-parse --verify --quiet "v${version}^{commit}") +if [[ -z "${releaseCommit}" ]]; then + # Without a commit, `gh run list` would return the latest run of the workflow, which may be another one. + printf "Fatal: the tag v%s cannot be resolved.\n" "${version}" + exit 1 +fi + +printf "Looking for the run of the workflow release.yml for the commit %s...\n" "${releaseCommit}" + +runId="" + +# The run can take a few seconds to appear after the push, so retry for a couple of minutes. +for _ in $(seq 1 12); do + runId=$(gh run list --repo element-hq/element-x-android --workflow release.yml --commit "${releaseCommit}" --limit 1 --json databaseId -q '.[0].databaseId' 2> /dev/null) + if [[ -n "${runId}" ]]; then + break + fi + printf "No run found yet, waiting...\n" + sleep 10 +done + +if [[ -z "${runId}" ]]; then + printf "Fatal: no run of the workflow release.yml found for the commit %s.\n" "${releaseCommit}" + exit 1 +fi + +printf "Found the run https://github.com/element-hq/element-x-android/actions/runs/%s\n" "${runId}" +printf "Waiting for the run to complete...\n" +gh run watch "${runId}" --repo element-hq/element-x-android --compact --exit-status + +ret=1 + +while [[ $ret -ne 0 ]]; do + gh run download "${runId}" --repo element-hq/element-x-android \ + --dir "${gplayTargetPath}" \ + --name elementx-app-gplay-bundle-unsigned + + ret=$? + if [[ $ret -eq 0 ]]; then + gh run download "${runId}" --repo element-hq/element-x-android \ + --dir "${fdroidTargetPath}" \ + --name elementx-app-fdroid-apks-unsigned + + ret=$? + fi + if [[ $ret -ne 0 ]]; then + read -r -p "Error while downloading the artifacts. You may want to fix the issue and retry. Retry (yes/no) default to yes? " doRetry + doRetry=${doRetry:-yes} + if [ "${doRetry}" == "no" ]; then + exit 1 + fi + fi +done + +printf "\n================================================================================\n" +printf "Signing the FDroid APKs...\n" + +cp "${fdroidTargetPath}"/app-fdroid-arm64-v8a-release.apk \ + "${fdroidTargetPath}"/app-fdroid-arm64-v8a-release-signed.apk +"${buildToolsPath}"/apksigner sign \ + -v \ + --alignment-preserved true \ + --ks "${keyStorePath}" \ + --ks-pass pass:"${keyStorePassword}" \ + --ks-key-alias elementx \ + --key-pass pass:"${keyPassword}" \ + --min-sdk-version "${minSdkVersion}" \ + "${fdroidTargetPath}"/app-fdroid-arm64-v8a-release-signed.apk + +cp "${fdroidTargetPath}"/app-fdroid-armeabi-v7a-release.apk \ + "${fdroidTargetPath}"/app-fdroid-armeabi-v7a-release-signed.apk +"${buildToolsPath}"/apksigner sign \ + -v \ + --alignment-preserved true \ + --ks "${keyStorePath}" \ + --ks-pass pass:"${keyStorePassword}" \ + --ks-key-alias elementx \ + --key-pass pass:"${keyPassword}" \ + --min-sdk-version "${minSdkVersion}" \ + "${fdroidTargetPath}"/app-fdroid-armeabi-v7a-release-signed.apk + +cp "${fdroidTargetPath}"/app-fdroid-x86-release.apk \ + "${fdroidTargetPath}"/app-fdroid-x86-release-signed.apk +"${buildToolsPath}"/apksigner sign \ + -v \ + --alignment-preserved true \ + --ks "${keyStorePath}" \ + --ks-pass pass:"${keyStorePassword}" \ + --ks-key-alias elementx \ + --key-pass pass:"${keyPassword}" \ + --min-sdk-version "${minSdkVersion}" \ + "${fdroidTargetPath}"/app-fdroid-x86-release-signed.apk + +cp "${fdroidTargetPath}"/app-fdroid-x86_64-release.apk \ + "${fdroidTargetPath}"/app-fdroid-x86_64-release-signed.apk +"${buildToolsPath}"/apksigner sign \ + -v \ + --alignment-preserved true \ + --ks "${keyStorePath}" \ + --ks-pass pass:"${keyStorePassword}" \ + --ks-key-alias elementx \ + --key-pass pass:"${keyPassword}" \ + --min-sdk-version "${minSdkVersion}" \ + "${fdroidTargetPath}"/app-fdroid-x86_64-release-signed.apk + +printf "\n================================================================================\n" +printf "Checking the signed APKs...\n" + +checkError=0 + +# Each APK gets the version code of the app bundle, plus the code of its ABI. +# Must be kept in sync with the abiVersionCodes map in app/build.gradle.kts. +fdroidAbis="armeabi-v7a:1 arm64-v8a:2 x86:3 x86_64:4" + +for abiEntry in ${fdroidAbis}; do + abi="${abiEntry%%:*}" + abiCode="${abiEntry##*:}" + expectedApkVersionCode=$((versionCode + abiCode)) + apkFile="${fdroidTargetPath}/app-fdroid-${abi}-release-signed.apk" + apkBadging=$("${buildToolsPath}"/aapt dump badging "${apkFile}" | grep -m 1 "^package") + apkVersionCode=$(printf "%s" "${apkBadging}" | sed -n "s/.*versionCode='\([0-9]*\)'.*/\1/p") + apkVersionName=$(printf "%s" "${apkBadging}" | sed -n "s/.*versionName='\([^']*\)'.*/\1/p") + printf "File app-fdroid-%s-release-signed.apk: version code %s, version name %s\n" "${abi}" "${apkVersionCode}" "${apkVersionName}" + if [[ "${apkVersionCode}" != "${expectedApkVersionCode}" ]]; then + printf "Warning: was expecting the version code %s, but got %s.\n" "${expectedApkVersionCode}" "${apkVersionCode}" + checkError=1 + fi + if [[ "${apkVersionName}" != "${version}" ]]; then + printf "Warning: was expecting the version name %s, but got %s.\n" "${version}" "${apkVersionName}" + checkError=1 + fi +done + +checkVersionResult + +printf "\n================================================================================\n" +printf "The APKs in %s have been signed!\n" "${fdroidTargetPath}" + +unsignedBundlePath="${gplayTargetPath}/app-gplay-release.aab" +signedBundlePath="${gplayTargetPath}/app-gplay-release-signed.aab" +# The universal APK is downloaded manually from the GooglePlay console, and is named after the version code. +universalApkPath="${gplayTargetPath}/${versionCode}.apk" + +printf "\n================================================================================\n" +printf "Signing file %s with build-tools version %s for min SDK version %s...\n" "${unsignedBundlePath}" "${buildToolsVersion}" "${minSdkVersion}" + +cp "${unsignedBundlePath}" "${signedBundlePath}" + +"${buildToolsPath}"/apksigner sign \ + -v \ + --ks "${keyStorePath}" \ + --ks-pass pass:"${keyStorePassword}" \ + --ks-key-alias elementx \ + --key-pass pass:"${keyPassword}" \ + --min-sdk-version "${minSdkVersion}" \ + "${signedBundlePath}" + +printf "\n================================================================================\n" +printf "Checking the signed app bundle...\n" + +checkError=0 + +bundleVersionCode=$(bundletool dump manifest --bundle="${signedBundlePath}" --xpath=/manifest/@android:versionCode) +bundleVersionName=$(bundletool dump manifest --bundle="${signedBundlePath}" --xpath=/manifest/@android:versionName) +printf "File %s: version code %s, version name %s\n" "$(basename "${signedBundlePath}")" "${bundleVersionCode}" "${bundleVersionName}" +if [[ "${bundleVersionCode}" != "${versionCode}" ]]; then + printf "Warning: was expecting the version code %s, but got %s.\n" "${versionCode}" "${bundleVersionCode}" + checkError=1 +fi +if [[ "${bundleVersionName}" != "${version}" ]]; then + printf "Warning: was expecting the version name %s, but got %s.\n" "${version}" "${bundleVersionName}" + checkError=1 +fi + +checkVersionResult + +printf "\n================================================================================\n" +printf "The file %s has been signed and can be uploaded to the PlayStore!\n" "${signedBundlePath}" + +printf "\n================================================================================\n" +read -r -p "Do you want to build the APKs from the app bundle? You need to do this step if you want to install the application to your device. (yes/no) default to no " doBuildApks +doBuildApks=${doBuildApks:-no} + +if [ "${doBuildApks}" == "yes" ]; then + printf "Building apks...\n" + bundletool build-apks --bundle="${signedBundlePath}" --output="${gplayTargetPath}"/elementx.apks \ + --ks=./app/signature/debug.keystore --ks-pass=pass:android --ks-key-alias=androiddebugkey --key-pass=pass:android \ + --overwrite + + read -r -p "Do you want to install the application to your device? Make sure there is one (and only one!) connected device first. (yes/no) default to yes " doDeploy + doDeploy=${doDeploy:-yes} + if [ "${doDeploy}" == "yes" ]; then + printf "Installing apk for your device...\n" + bundletool install-apks --apks="${gplayTargetPath}"/elementx.apks + read -r -p "Please run the application on your phone to check that the upgrade went well. Press enter to continue. " + else + printf "APK will not be deployed!\n" + fi +else + printf "APKs will not be generated!\n" +fi + +printf "\n================================================================================\n" +printf "Create the open testing release on GooglePlay.\n" + +printf "On GooglePlay console, go the the open testing section and click on \"Create new release\" button, then:\n" +printf " - upload the file %s.\n" "${signedBundlePath}" +printf " - copy the release note from the fastlane file.\n" +printf " - download the universal APK, to be able to provide it to the GitHub release: click on the right arrow next to the \"App bundle\", then click on the \"Download\" tab, and download the \"Signed, universal APK\". Save it, without renaming it, to the folder %s. The expected final path is %s\n" "${gplayTargetPath}" "${universalApkPath}" +printf " - submit the release.\n" +read -r -p "Press enter to continue. " + +printf "You can then go to \"Publishing overview\" and send the new release for a review by Google.\n" +read -r -p "Press enter to continue. " + +printf "\n================================================================================\n" +printf "Creating the release on GitHub.\n" + +releaseAssets=( + "${signedBundlePath}" + "${universalApkPath}" + "${fdroidTargetPath}/app-fdroid-arm64-v8a-release-signed.apk" + "${fdroidTargetPath}/app-fdroid-armeabi-v7a-release-signed.apk" + "${fdroidTargetPath}/app-fdroid-x86-release-signed.apk" + "${fdroidTargetPath}/app-fdroid-x86_64-release-signed.apk" +) + +missingAsset=0 + +for releaseAsset in "${releaseAssets[@]}"; do + if [[ ! -f "${releaseAsset}" ]]; then + printf "Error: the file %s does not exist.\n" "${releaseAsset}" + missingAsset=1 + fi +done + +if [[ ${missingAsset} -ne 0 ]]; then + printf "Fatal: some files are missing, cannot create the GitHub release.\n" + exit 1 +fi + +printf "Creating the pre-release v%s and uploading the %d files, this can take a while...\n" "${version}" "${#releaseAssets[@]}" +gh release create "v${version}" \ + --repo element-hq/element-x-android \ + --title "Element X Android v${version}" \ + --generate-notes \ + --prerelease \ + --verify-tag \ + "${releaseAssets[@]}" + +if [[ $? -ne 0 ]]; then + printf "Fatal: error while creating the GitHub release.\n" + exit 1 +fi + +printf "The pre-release has been created: https://github.com/element-hq/element-x-android/releases/tag/v%s\n" "${version}" +printf "Please check the generated release notes, and optionally reorder items and fix typos.\n" +read -r -p "Press enter to continue. " + +printf "\n================================================================================\n" +printf "Update the project release notes:\n\n" + +printf "Getting the release notes from GitHub...\n" +releaseNotes=$(gh release view "v${version}" --repo element-hq/element-x-android --json body -q .body) + +if [[ $? -ne 0 || -z "${releaseNotes}" ]]; then + printf "Fatal: error while getting the release notes from GitHub.\n" + exit 1 +fi + +# GitHub returns the release notes with CRLF line endings, remove the CR. +releaseNotes="${releaseNotes//$'\r'/}" +changesTitle="Changes in Element X v${version}" +# Underline the title with as many '=' as there are characters in the title. +changesUnderline="${changesTitle//?/=}" +changesFile="./CHANGES.md" +changesFileBak="${changesFile}.bak" +mv "${changesFile}" "${changesFileBak}" +{ + printf "%s\n%s\n\n" "${changesTitle}" "${changesUnderline}" + printf "%s\n\n" "${releaseNotes}" + cat "${changesFileBak}" +} > "${changesFile}" +rm "${changesFileBak}" +printf "The file CHANGES.md has been updated.\n" +read -r -p "Please check the change and press enter to commit it. " + +printf "\n================================================================================\n" +printf "Committing...\n" +git commit -a -m "Changelog for version ${version}" + +printf "\n================================================================================\n" +read -r -p "Done, push the branch 'develop' (yes/no) default to yes? (A rebase may be necessary in case develop got new commits) " doPush +doPush=${doPush:-yes} + +if [ "${doPush}" == "yes" ]; then + printf "Pushing branch 'develop'...\n" + git push origin develop +else + printf "Not pushing, do not forget to push manually!\n" +fi + +printf "\n================================================================================\n" +printf "Message for the Android internal room:\n\n" +message="@room Element X Android ${version} is ready to be tested. You can get it from https://github.com/element-hq/element-x-android/releases/tag/v${version}. You can install the universal APK. If you want to install the application from the app bundle, you can follow instructions [here](https://github.com/element-hq/element-x-android/blob/develop/docs/install_from_github_release.md). Please report any feedback. Thanks!" +printf "%s\n\n" "${message}" + +if [[ -z "${elementBotToken}" ]]; then + read -r -p "ELEMENT_BOT_MATRIX_TOKEN is not defined in the environment. Cannot send the message for you. Please send it manually, and press enter to continue. " +else + read -r -p "Send this message to the room (yes/no) default to yes? " doSend + doSend=${doSend:-yes} + if [ "${doSend}" == "yes" ]; then + printf "Sending message...\n" + transactionId=$(openssl rand -hex 16) + # Element Android internal + matrixRoomId="!LiSLXinTDCsepePiYW:matrix.org" + curl -X PUT --data "{\"msgtype\":\"m.text\",\"body\":\"${message}\"}" -H "Authorization: Bearer ${elementBotToken}" https://matrix-client.matrix.org/_matrix/client/r0/rooms/${matrixRoomId}/send/m.room.message/\$local."${transactionId}" + else + printf "Message not sent, please send it manually!\n" + fi +fi + +printf "\n================================================================================\n" +printf "Congratulation! Kudos for using this script! Have a nice day!\n" +printf "================================================================================\n" From 3758aac6d3c64e8b61c11da1704f6a8135e12c4c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Aug 2026 08:52:44 +0200 Subject: [PATCH 102/112] Do not build the enterprise build, it's built elsewhere. --- .github/workflows/release.yml | 38 ----------------------------------- 1 file changed, 38 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b9d8be16c9..a9cc829697 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -63,44 +63,6 @@ jobs: path: | app/build/outputs/bundle/gplayRelease/app-gplay-release.aab - enterprise: - name: Create App Bundle Enterprise - runs-on: ubuntu-latest - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} - concurrency: - group: ${{ format('build-release-main-enterprise-{0}', github.sha) }} - cancel-in-progress: true - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - name: Add SSH private keys for submodule repositories - uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} - with: - ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - - name: Clone submodules - run: git submodule update --init --recursive - - name: Use JDK 21 - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0 - with: - distribution: 'temurin' # See 'Supported distributions' for available options - java-version: '21' - - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 - - name: Create Enterprise app bundle - env: - ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} - ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} - ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} - run: ./gradlew bundleGplayRelease $CI_GRADLE_ARG_PROPERTIES - - name: Upload bundle as artifact - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: elementx-enterprise-app-gplay-bundle-unsigned - path: | - app/build/outputs/bundle/gplayRelease/app-gplay-release.aab - fdroid: name: Create APKs (FDroid) runs-on: ubuntu-latest From 6fbccbc8972d660282b7540167858e436bbe6c54 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Aug 2026 12:03:32 +0200 Subject: [PATCH 103/112] Update submodule ref. --- enterprise | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enterprise b/enterprise index e633a343b1..53750903f2 160000 --- a/enterprise +++ b/enterprise @@ -1 +1 @@ -Subproject commit e633a343b12088b1fe3fd6b27ec319c1f7f2931c +Subproject commit 53750903f229a410ec4dded2bb6357431a55662e From 2f3d197060ef7d83848fe31f4e7620479d78abae Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Aug 2026 14:20:36 +0200 Subject: [PATCH 104/112] Setting version for the release 26.08.4 --- plugins/src/main/kotlin/Versions.kt | 2 +- tools/manifest/gplay/release/aaptDump.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 3e4b4f5c7f..0c2885aaa1 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -45,7 +45,7 @@ private const val versionMonth = 8 * Release number in the month. Value must be in [0,99]. * Do not update this value. it is updated by the release script. */ -private const val versionReleaseNumber = 3 +private const val versionReleaseNumber = 4 object Versions { /** diff --git a/tools/manifest/gplay/release/aaptDump.txt b/tools/manifest/gplay/release/aaptDump.txt index 457a6ea1cc..ff8cec3036 100644 --- a/tools/manifest/gplay/release/aaptDump.txt +++ b/tools/manifest/gplay/release/aaptDump.txt @@ -1,4 +1,4 @@ -package: name='io.element.android.x' versionCode='202608030' versionName='26.08.3' platformBuildVersionName='17' platformBuildVersionCode='37' compileSdkVersion='37' compileSdkVersionCodename='17' +package: name='io.element.android.x' versionCode='202608040' versionName='26.08.4' platformBuildVersionName='17' platformBuildVersionCode='37' compileSdkVersion='37' compileSdkVersionCodename='17' sdkVersion:'24' targetSdkVersion:'37' uses-permission: name='android.permission.POST_NOTIFICATIONS' From 383519aff5b13597955b62470218de21007ad171 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Aug 2026 14:21:17 +0200 Subject: [PATCH 105/112] Adding fastlane file for version 26.08.4 --- fastlane/metadata/android/en-US/changelogs/202608040.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/202608040.txt diff --git a/fastlane/metadata/android/en-US/changelogs/202608040.txt b/fastlane/metadata/android/en-US/changelogs/202608040.txt new file mode 100644 index 0000000000..a4b397f1bb --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/202608040.txt @@ -0,0 +1,2 @@ +Main changes in this version: bug fixes and improvements. +Full changelog: https://github.com/element-hq/element-x-android/releases \ No newline at end of file From 4996819812cee98a995123734c2ffc2ba1c0cf1b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Aug 2026 14:53:48 +0200 Subject: [PATCH 106/112] Changelog for version 26.08.4 --- CHANGES.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index a56dd4d1bb..5f4be5d6eb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,23 @@ +Changes in Element X v26.08.4 +============================= + + + +## What's Changed +### 🧱 Build +* Add releaseV2.sh, a release script automating the GitHub release by @bmarty in https://github.com/element-hq/element-x-android/pull/7579 +* Update submodule ref. by @bmarty in https://github.com/element-hq/element-x-android/pull/7582 +### Others +* Keep the formatting of a caption that is being edited by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7572 +* Say when the server cannot be reached instead of showing nothing by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7567 +* Keep saving files to Downloads once 32 files share the same name by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7344 +* Fix removed messages hiding its thread on deletion by @bxdxnn in https://github.com/element-hq/element-x-android/pull/7480 +* Drop the sender's name from the room list preview of two-person rooms by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7562 +* Document how the Element Call base URL developer option is used by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7546 + + +**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.08.3...v26.08.4 + Changes in Element X v26.08.3 ============================= From 8645260d4691b5ceef6efb8417201a78e4b3df9d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Aug 2026 14:58:39 +0200 Subject: [PATCH 107/112] Do not open the editor when finishing the release `git flow release finish` opened the editor three times, and the content was never edited: twice for the merge commits, where the message generated by git is always kept, and once for the message of the annotated tag, where a placeholder was typed by hand. The last tags carry the message "tag" for that reason. GIT_MERGE_AUTOEDIT=no keeps the merge messages git already generates, and -m provides the tag message. git flow appends the tag name to that message, so the tag of the next release will read "Release v26.09.0". Co-Authored-By: Claude Opus 5 (1M context) --- tools/release/releaseV2.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/release/releaseV2.sh b/tools/release/releaseV2.sh index 071d416d00..cef4b7e6b0 100755 --- a/tools/release/releaseV2.sh +++ b/tools/release/releaseV2.sh @@ -188,7 +188,10 @@ git commit -a -m "Adding fastlane file for version ${version}" printf "\n================================================================================\n" printf "OK, finishing the release...\n" -git flow release finish "${version}" +# GIT_MERGE_AUTOEDIT avoids opening the editor for the 2 merge commits, whose default message is +# always used, and -m provides the message of the annotated tag. git flow appends the tag name to +# it, so the tag message ends up being "Release v${version}". +GIT_MERGE_AUTOEDIT=no git flow release finish -m "Release" "${version}" printf "\n================================================================================\n" read -r -p "Done, push the branch 'main' and the new tag (yes/no) default to yes? " doPush From 586ae00cda27725314b51a336d06c142426a6ef8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Aug 2026 15:54:43 +0200 Subject: [PATCH 108/112] Ensure that noisy notifications are still noisy when content is hidden when a PIN code is protecting the application. Closes #7575 --- .../test/notification/NotificationData.kt | 3 +- .../DefaultNotifiableEventResolver.kt | 1 + .../DefaultNotificationDrawerManager.kt | 7 + .../FallbackNotificationFactory.kt | 2 + .../NotificationResultProcessor.kt | 1 + .../factories/NotificationCreator.kt | 2 +- .../model/FallbackNotifiableEvent.kt | 1 + .../DefaultNotifiableEventResolverTest.kt | 35 +++++ .../DefaultNotificationDrawerManagerTest.kt | 143 +++++++++++++++++- .../DefaultNotificationResultProcessorTest.kt | 36 +++++ .../FallbackNotificationFactoryTest.kt | 58 +++++++ .../DefaultNotificationCreatorTest.kt | 55 ++++++- .../fixtures/NotifiableEventFixture.kt | 33 ++-- 13 files changed, 362 insertions(+), 15 deletions(-) create mode 100644 libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/FallbackNotificationFactoryTest.kt diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/NotificationData.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/NotificationData.kt index 803c9d8f42..6e57100781 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/NotificationData.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/NotificationData.kt @@ -30,6 +30,7 @@ fun aNotificationData( senderIsNameAmbiguous: Boolean = false, roomDisplayName: String? = A_ROOM_NAME, roomJoinRule: JoinRule? = null, + isNoisy: Boolean = false, ): NotificationData { return NotificationData( sessionId = A_SESSION_ID, @@ -45,7 +46,7 @@ fun aNotificationData( isDm = false, isSpace = isSpace, isEncrypted = false, - isNoisy = false, + isNoisy = isNoisy, timestamp = timestamp, content = content, hasMention = hasMention, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt index fdffad3049..e22ae1f7d1 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt @@ -298,6 +298,7 @@ class DefaultNotifiableEventResolver( roomId = roomId, eventId = eventId, cause = "Unable to decrypt event content", + noisy = isNoisy, ) ResolvedPushEvent.Event(fallbackNotifiableEvent) } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt index b6d15dac4e..6834f404ed 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt @@ -274,6 +274,12 @@ private fun NotifiableEvent.toFallbackNotifiableEvent(): FallbackNotifiableEvent is FallbackNotifiableEvent -> timestamp is NotifiableRingingCallEvent -> return null } + val noisy = when (this) { + is NotifiableMessageEvent -> noisy + is InviteNotifiableEvent -> noisy + is SimpleNotifiableEvent -> noisy + is FallbackNotifiableEvent -> noisy + } return FallbackNotifiableEvent( sessionId = sessionId, roomId = roomId, @@ -283,6 +289,7 @@ private fun NotifiableEvent.toFallbackNotifiableEvent(): FallbackNotifiableEvent canBeReplaced = false, isRedacted = false, isUpdated = false, + noisy = noisy, timestamp = timestamp, cause = null, ) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/FallbackNotificationFactory.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/FallbackNotificationFactory.kt index 970f19fd45..d2083f7333 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/FallbackNotificationFactory.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/FallbackNotificationFactory.kt @@ -24,6 +24,7 @@ class FallbackNotificationFactory( roomId: RoomId, eventId: EventId, cause: String?, + noisy: Boolean, ): FallbackNotifiableEvent = FallbackNotifiableEvent( sessionId = sessionId, roomId = roomId, @@ -32,6 +33,7 @@ class FallbackNotificationFactory( canBeReplaced = true, isRedacted = false, isUpdated = false, + noisy = noisy, timestamp = clock.epochMillis(), description = "", cause = cause, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationResultProcessor.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationResultProcessor.kt index a97937f5d0..88ba6c5ff9 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationResultProcessor.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationResultProcessor.kt @@ -170,6 +170,7 @@ class DefaultNotificationResultProcessor( roomId = RoomId(request.roomId), eventId = EventId(request.eventId), cause = exception.message, + noisy = false, ) ) } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt index afbbe4af4f..26412abebc 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt @@ -317,7 +317,7 @@ class DefaultNotificationCreator( val fallbackNotifiableEvent = fallbackNotifiableEvents.first() val channelId = notificationChannels.getChannelIdForMessage( sessionId = fallbackNotifiableEvent.sessionId, - noisy = false, + noisy = fallbackNotifiableEvents.any { it.noisy }, ) val existingCounter = existingNotification ?.extras diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/FallbackNotifiableEvent.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/FallbackNotifiableEvent.kt index 97f66face0..3b3210a580 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/FallbackNotifiableEvent.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/FallbackNotifiableEvent.kt @@ -25,6 +25,7 @@ data class FallbackNotifiableEvent( override val canBeReplaced: Boolean, override val isRedacted: Boolean, override val isUpdated: Boolean, + val noisy: Boolean, val timestamp: Long, val cause: String?, ) : NotifiableEvent diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt index 24e88e72d2..5918329c40 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt @@ -712,6 +712,41 @@ class DefaultNotifiableEventResolverTest : RobolectricTest() { canBeReplaced = true, isRedacted = false, isUpdated = false, + noisy = false, + timestamp = A_FAKE_TIMESTAMP, + cause = "Unable to decrypt event content", + ) + ) + assertThat(result.getEvent(request)).isEqualTo(Result.success(expectedResult)) + } + + @Test + fun `resolve RoomEncrypted noisy`() = runTest { + val sut = createDefaultNotifiableEventResolver( + notificationResult = Result.success( + mapOf( + AN_EVENT_ID to Result.success( + aNotificationData( + content = NotificationContent.MessageLike.RoomEncrypted, + isNoisy = true, + ) + ) + ) + ) + ) + val request = aPushRequest(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID, "firebase") + val result = sut.resolveEvents(A_SESSION_ID, listOf(request)) + val expectedResult = ResolvedPushEvent.Event( + FallbackNotifiableEvent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + editedEventId = null, + description = "", + canBeReplaced = true, + isRedacted = false, + isUpdated = false, + noisy = true, timestamp = A_FAKE_TIMESTAMP, cause = "Unable to decrypt event content", ) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt index 4e2685e63a..cdc6a01fad 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt @@ -15,28 +15,35 @@ import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.features.lockscreen.test.FakeLockScreenService +import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.AN_EVENT_ID_2 import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID_2 import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_SESSION_ID_2 import io.element.android.libraries.matrix.test.A_THREAD_ID import io.element.android.libraries.matrix.test.A_THREAD_ID_2 +import io.element.android.libraries.matrix.test.A_TIMESTAMP import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClientProvider import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.media.test.FakeImageLoaderHolder import io.element.android.libraries.push.api.notifications.NotificationIdProvider +import io.element.android.libraries.push.impl.notifications.factories.NotificationAccountParams +import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator import io.element.android.libraries.push.impl.notifications.factories.aNotificationAccountParams import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotificationsProvider import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationCreator import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDisplayer import io.element.android.libraries.push.impl.notifications.fake.FakeRoomGroupMessageCreator import io.element.android.libraries.push.impl.notifications.fake.FakeSummaryGroupMessageCreator +import io.element.android.libraries.push.impl.notifications.fixtures.A_NOTIFICATION import io.element.android.libraries.push.impl.notifications.fixtures.aFallbackNotifiableEvent import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent import io.element.android.libraries.push.impl.notifications.fixtures.aSimpleNotifiableEvent import io.element.android.libraries.push.impl.notifications.fixtures.anInviteNotifiableEvent +import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.api.observer.SessionObserver @@ -48,6 +55,8 @@ import io.element.android.services.appnavstate.api.AppNavigationStateService import io.element.android.services.appnavstate.test.FakeAppNavigationStateService import io.element.android.services.appnavstate.test.aNavigationState import io.element.android.services.appnavstate.test.anAppNavigationState +import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP +import io.element.android.tests.testutils.lambda.LambdaThreeParamsRecorder import io.element.android.tests.testutils.lambda.any import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value @@ -472,6 +481,137 @@ class DefaultNotificationDrawerManagerTest { extraInvocationsForNotificationSummary = 1, ) + @Test + fun `when the app is locked, a noisy message event produces a noisy fallback notification`() = testAppLockedNotifiableEventReceived( + notifiableEvent = aNotifiableMessageEvent(noisy = true), + expectedFallbackEvent = anExpectedFallbackNotifiableEvent(noisy = true, timestamp = A_TIMESTAMP), + ) + + @Test + fun `when the app is locked, a silent message event produces a silent fallback notification`() = testAppLockedNotifiableEventReceived( + notifiableEvent = aNotifiableMessageEvent(noisy = false), + expectedFallbackEvent = anExpectedFallbackNotifiableEvent(noisy = false, timestamp = A_TIMESTAMP), + ) + + @Test + fun `when the app is locked, a noisy invite event produces a noisy fallback notification`() = testAppLockedNotifiableEventReceived( + notifiableEvent = anInviteNotifiableEvent(noisy = true), + expectedFallbackEvent = anExpectedFallbackNotifiableEvent(noisy = true, timestamp = 0), + ) + + @Test + fun `when the app is locked, a silent invite event produces a silent fallback notification`() = testAppLockedNotifiableEventReceived( + notifiableEvent = anInviteNotifiableEvent(noisy = false), + expectedFallbackEvent = anExpectedFallbackNotifiableEvent(noisy = false, timestamp = 0), + ) + + @Test + fun `when the app is locked, a noisy simple event produces a noisy fallback notification`() = testAppLockedNotifiableEventReceived( + notifiableEvent = aSimpleNotifiableEvent(noisy = true), + expectedFallbackEvent = anExpectedFallbackNotifiableEvent(noisy = true, timestamp = 0), + ) + + @Test + fun `when the app is locked, a silent simple event produces a silent fallback notification`() = testAppLockedNotifiableEventReceived( + notifiableEvent = aSimpleNotifiableEvent(noisy = false), + expectedFallbackEvent = anExpectedFallbackNotifiableEvent(noisy = false, timestamp = 0), + ) + + @Test + fun `when the app is locked, a noisy fallback event stays noisy`() = testAppLockedNotifiableEventReceived( + notifiableEvent = aFallbackNotifiableEvent(noisy = true), + expectedFallbackEvent = anExpectedFallbackNotifiableEvent(noisy = true, timestamp = A_FAKE_TIMESTAMP), + ) + + @Test + fun `when the app is locked, a silent fallback event stays silent`() = testAppLockedNotifiableEventReceived( + notifiableEvent = aFallbackNotifiableEvent(noisy = false), + expectedFallbackEvent = anExpectedFallbackNotifiableEvent(noisy = false, timestamp = A_FAKE_TIMESTAMP), + ) + + @Test + fun `when the app is locked, all the events are grouped into a single fallback notification`() = runTest { + val createFallbackNotificationResult = aCreateFallbackNotificationRecorder() + val sut = createAppLockedNotificationDrawerManager(createFallbackNotificationResult) + sut.onNotifiableEventsReceived( + listOf( + aNotifiableMessageEvent(noisy = false), + aNotifiableMessageEvent(eventId = AN_EVENT_ID_2, noisy = true), + ) + ) + createFallbackNotificationResult.assertions() + .isCalledOnce() + .with( + value(null), + any(), + value( + listOf( + anExpectedFallbackNotifiableEvent(noisy = false, timestamp = A_TIMESTAMP), + anExpectedFallbackNotifiableEvent(eventId = AN_EVENT_ID_2, noisy = true, timestamp = A_TIMESTAMP), + ) + ), + ) + } + + @Test + fun `when the app is not locked, no fallback notification is created`() = runTest { + val createFallbackNotificationResult = aCreateFallbackNotificationRecorder() + val sut = createDefaultNotificationDrawerManager( + appNavigationStateService = FakeAppNavigationStateService( + initialAppNavigationState = anAppNavigationState(isInForeground = false), + ), + notificationCreator = FakeNotificationCreator( + createFallbackNotificationResult = createFallbackNotificationResult, + ), + lockScreenService = FakeLockScreenService().apply { setIsPinSetup(false) }, + ) + sut.onNotifiableEventsReceived(listOf(aNotifiableMessageEvent(noisy = true))) + createFallbackNotificationResult.assertions().isNeverCalled() + } + + /** + * The content of the original event must be stripped, only the noisy flag, the ids and the timestamp are kept. + */ + private fun anExpectedFallbackNotifiableEvent( + eventId: EventId = AN_EVENT_ID, + noisy: Boolean, + timestamp: Long, + ) = aFallbackNotifiableEvent( + eventId = eventId, + description = null, + canBeReplaced = false, + noisy = noisy, + timestamp = timestamp, + cause = null, + ) + + private fun aCreateFallbackNotificationRecorder() = + lambdaRecorder, Notification> { _, _, _ -> A_NOTIFICATION } + + private fun TestScope.createAppLockedNotificationDrawerManager( + createFallbackNotificationResult: LambdaThreeParamsRecorder, Notification>, + ) = createDefaultNotificationDrawerManager( + appNavigationStateService = FakeAppNavigationStateService( + initialAppNavigationState = anAppNavigationState(isInForeground = false), + ), + notificationCreator = FakeNotificationCreator( + createFallbackNotificationResult = createFallbackNotificationResult, + ), + lockScreenService = FakeLockScreenService().apply { setIsPinSetup(true) }, + ) + + private fun testAppLockedNotifiableEventReceived( + notifiableEvent: NotifiableEvent, + expectedFallbackEvent: FallbackNotifiableEvent, + ) = runTest { + val createFallbackNotificationResult = aCreateFallbackNotificationRecorder() + val sut = createAppLockedNotificationDrawerManager(createFallbackNotificationResult) + sut.onNotifiableEventsReceived(listOf(notifiableEvent)) + createFallbackNotificationResult.assertions() + .isCalledOnce() + .with(value(null), any(), value(listOf(expectedFallbackEvent))) + } + private fun testOnNotifiableEventReceived( appNavigationState: AppNavigationState, notifiableEvents: List, @@ -503,6 +643,7 @@ class DefaultNotificationDrawerManagerTest { fun TestScope.createDefaultNotificationDrawerManager( notificationDisplayer: NotificationDisplayer = FakeNotificationDisplayer(), notificationRenderer: NotificationRenderer? = null, + notificationCreator: NotificationCreator = FakeNotificationCreator(), appNavigationStateService: AppNavigationStateService = FakeAppNavigationStateService(), roomGroupMessageCreator: RoomGroupMessageCreator = FakeRoomGroupMessageCreator(), summaryGroupMessageCreator: SummaryGroupMessageCreator = FakeSummaryGroupMessageCreator(), @@ -519,7 +660,7 @@ fun TestScope.createDefaultNotificationDrawerManager( notificationRenderer = notificationRenderer ?: NotificationRenderer( notificationDisplayer = notificationDisplayer, notificationDataFactory = DefaultNotificationDataFactory( - notificationCreator = FakeNotificationCreator(), + notificationCreator = notificationCreator, roomGroupMessageCreator = roomGroupMessageCreator, summaryGroupMessageCreator = summaryGroupMessageCreator, activeNotificationsProvider = activeNotificationsProvider, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationResultProcessorTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationResultProcessorTest.kt index 91f29dd28e..21a2c6cb91 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationResultProcessorTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationResultProcessorTest.kt @@ -267,6 +267,42 @@ class DefaultNotificationResultProcessorTest { assertThat(receivedFallbackEvent).isTrue() } + @Test + fun `when an event cannot be resolved, the generated fallback event is silent`() = runTest { + val onNotifiableEventsReceived = lambdaRecorder, Unit> {} + val onPushReceivedResult = lambdaRecorder { _, _, _, _, _, _, _ -> } + val processor = createDefaultNotificationResultProcessor( + mutableBatteryOptimizationStore = FakeMutableBatteryOptimizationStore( + showBatteryOptimizationBannerResult = lambdaRecorder {}, + ), + pushHistoryService = FakePushHistoryService( + onPushReceivedResult = onPushReceivedResult, + ), + onNotifiableEventsReceived = onNotifiableEventsReceived, + ) + + runningProcessor(processor) { + emit(mapOf(aPushRequest() to Result.failure(NotificationResolverException.UnknownError("Unable to resolve event")))) + } + + advanceTimeBy(300.milliseconds) + + onNotifiableEventsReceived.assertions() + .isCalledOnce() + .with( + value( + listOf( + aFallbackNotifiableEvent( + description = "", + canBeReplaced = true, + noisy = false, + cause = "Unable to resolve event", + ) + ) + ) + ) + } + private suspend fun TestScope.runningProcessor(processor: NotificationResultProcessor, block: suspend NotificationResultProcessor.() -> Unit) { processor.start() diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/FallbackNotificationFactoryTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/FallbackNotificationFactoryTest.kt new file mode 100644 index 0000000000..58825f07b9 --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/FallbackNotificationFactoryTest.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.push.impl.notifications + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.push.impl.notifications.fixtures.aFallbackNotifiableEvent +import io.element.android.services.toolbox.test.systemclock.FakeSystemClock +import org.junit.Test + +class FallbackNotificationFactoryTest { + @Test + fun `create silent event`() { + val sut = FallbackNotificationFactory(FakeSystemClock()) + val result = sut.create( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + cause = "A cause", + noisy = false, + ) + assertThat(result).isEqualTo( + aFallbackNotifiableEvent( + description = "", + canBeReplaced = true, + noisy = false, + cause = "A cause", + ) + ) + } + + @Test + fun `create noisy event`() { + val sut = FallbackNotificationFactory(FakeSystemClock()) + val result = sut.create( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + cause = "A cause", + noisy = true, + ) + assertThat(result).isEqualTo( + aFallbackNotifiableEvent( + description = "", + canBeReplaced = true, + noisy = true, + cause = "A cause", + ) + ) + } +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt index 299c5ad213..ead16c02d7 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt @@ -18,6 +18,7 @@ import io.element.android.appconfig.NotificationConfig import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.AN_EVENT_ID_2 import io.element.android.libraries.matrix.test.A_COLOR_INT @@ -33,6 +34,7 @@ import io.element.android.libraries.push.impl.notifications.DefaultNotificationB import io.element.android.libraries.push.impl.notifications.NotificationActionIds import io.element.android.libraries.push.impl.notifications.RoomEventGroupInfo import io.element.android.libraries.push.impl.notifications.channels.DefaultNotificationChannels +import io.element.android.libraries.push.impl.notifications.channels.FakeNotificationChannels import io.element.android.libraries.push.impl.notifications.channels.NotificationChannels import io.element.android.libraries.push.impl.notifications.factories.action.AcceptInvitationActionFactory import io.element.android.libraries.push.impl.notifications.factories.action.MarkAsReadActionFactory @@ -47,6 +49,8 @@ import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvid import io.element.android.services.toolbox.test.strings.FakeStringProvider import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP import io.element.android.services.toolbox.test.systemclock.FakeSystemClock +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.robolectric.RobolectricTest import kotlinx.coroutines.test.runTest import org.junit.Test @@ -82,7 +86,10 @@ class DefaultNotificationCreatorTest : RobolectricTest() { @Test fun `test createFallbackNotification`() { - val sut = createNotificationCreator() + val channelIdForMessage = lambdaRecorder { _, _ -> A_CHANNEL_ID } + val sut = createNotificationCreator( + notificationChannels = FakeNotificationChannels(channelIdForMessage = channelIdForMessage), + ) val result = sut.createFallbackNotification( existingNotification = null, notificationAccountParams = aNotificationAccountParams(), @@ -93,6 +100,50 @@ class DefaultNotificationCreatorTest : RobolectricTest() { result.commonAssertions( expectedCategory = null, ) + assertThat(result.channelId).isEqualTo(A_CHANNEL_ID) + // The notification must be posted on the silent channel. + channelIdForMessage.assertions().isCalledOnce().with(value(A_SESSION_ID), value(false)) + } + + @Test + fun `test createFallbackNotification noisy`() { + val channelIdForMessage = lambdaRecorder { _, _ -> A_CHANNEL_ID } + val sut = createNotificationCreator( + notificationChannels = FakeNotificationChannels(channelIdForMessage = channelIdForMessage), + ) + val result = sut.createFallbackNotification( + existingNotification = null, + notificationAccountParams = aNotificationAccountParams(), + fallbackNotifiableEvents = listOf( + aFallbackNotifiableEvent(noisy = true), + ) + ) + result.commonAssertions( + expectedCategory = null, + ) + assertThat(result.channelId).isEqualTo(A_CHANNEL_ID) + // The notification must be posted on the noisy channel. + channelIdForMessage.assertions().isCalledOnce().with(value(A_SESSION_ID), value(true)) + } + + @Test + fun `test createFallbackNotification is noisy when at least one event is noisy`() { + val channelIdForMessage = lambdaRecorder { _, _ -> A_CHANNEL_ID } + val sut = createNotificationCreator( + notificationChannels = FakeNotificationChannels(channelIdForMessage = channelIdForMessage), + ) + val result = sut.createFallbackNotification( + existingNotification = null, + notificationAccountParams = aNotificationAccountParams(), + fallbackNotifiableEvents = listOf( + aFallbackNotifiableEvent(noisy = false), + aFallbackNotifiableEvent(eventId = AN_EVENT_ID_2, noisy = true), + ) + ) + result.commonAssertions( + expectedCategory = null, + ) + channelIdForMessage.assertions().isCalledOnce().with(value(A_SESSION_ID), value(true)) } @Test @@ -389,6 +440,8 @@ class DefaultNotificationCreatorTest : RobolectricTest() { } } +private const val A_CHANNEL_ID = "aChannelId" + const val MARK_AS_READ_ACTION_TITLE = "MarkAsReadAction" const val QUICK_REPLY_ACTION_TITLE = "QuickReplyAction" const val ACCEPT_INVITATION_ACTION_TITLE = "AcceptInvitationAction" diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt index 4242632fe5..cb4bffcfb3 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt @@ -39,13 +39,14 @@ fun aSimpleNotifiableEvent( type: String? = null, isRedacted: Boolean = false, canBeReplaced: Boolean = false, - editedEventId: EventId? = null + editedEventId: EventId? = null, + noisy: Boolean = false, ) = SimpleNotifiableEvent( sessionId = sessionId, roomId = roomId, eventId = eventId, editedEventId = editedEventId, - noisy = false, + noisy = noisy, title = "title", description = "description", type = type, @@ -59,14 +60,15 @@ fun anInviteNotifiableEvent( sessionId: SessionId = A_SESSION_ID, roomId: RoomId = A_ROOM_ID, eventId: EventId = AN_EVENT_ID, - isRedacted: Boolean = false + isRedacted: Boolean = false, + noisy: Boolean = false, ) = InviteNotifiableEvent( sessionId = sessionId, eventId = eventId, roomId = roomId, roomName = A_ROOM_NAME, editedEventId = null, - noisy = false, + noisy = noisy, title = "title", description = "description", type = null, @@ -89,11 +91,12 @@ fun aNotifiableMessageEvent( senderId: UserId = A_USER_ID_2, senderDisambiguatedDisplayName: String = A_USER_NAME_2, roomName: String? = A_ROOM_NAME, + noisy: Boolean = false, ) = NotifiableMessageEvent( sessionId = sessionId, eventId = eventId, editedEventId = null, - noisy = false, + noisy = noisy, timestamp = timestamp, senderDisambiguatedDisplayName = senderDisambiguatedDisplayName, senderId = senderId, @@ -149,15 +152,23 @@ fun aNotifiableCallEvent( fun aFallbackNotifiableEvent( sessionId: SessionId = A_SESSION_ID, + roomId: RoomId = A_ROOM_ID, + eventId: EventId = AN_EVENT_ID, + description: String? = "A fallback notification", + canBeReplaced: Boolean = false, + noisy: Boolean = false, + timestamp: Long = A_FAKE_TIMESTAMP, + cause: String? = "Unable to decrypt event", ) = FallbackNotifiableEvent( sessionId = sessionId, - roomId = A_ROOM_ID, - eventId = AN_EVENT_ID, + roomId = roomId, + eventId = eventId, editedEventId = null, - description = "A fallback notification", - canBeReplaced = false, + description = description, + canBeReplaced = canBeReplaced, isRedacted = false, isUpdated = false, - timestamp = A_FAKE_TIMESTAMP, - cause = "Unable to decrypt event", + noisy = noisy, + timestamp = timestamp, + cause = cause, ) From f0c88eb12e7d8bbf775c09836ae75533207bc79f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 18:47:22 +0200 Subject: [PATCH 109/112] Update dependency net.zetetic:sqlcipher-android to v4.18.0 (#7552) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c22264eaf1..444ac05d8e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -208,7 +208,7 @@ matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-jvm = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "sqldelight" } sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" } -sqlcipher = "net.zetetic:sqlcipher-android:4.17.0" +sqlcipher = "net.zetetic:sqlcipher-android:4.18.0" sqlite = "androidx.sqlite:sqlite-ktx:2.7.0" unifiedpush = "org.unifiedpush.android:connector:3.3.3" vanniktech_blurhash = "com.vanniktech:blurhash:0.3.0" From 616ed92d00aa025045ef0494351b18863acb5a4e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 18:47:58 +0200 Subject: [PATCH 110/112] Update dependency com.google.firebase:firebase-bom to v34.18.0 (#7581) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 444ac05d8e..b129d1cf27 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -84,7 +84,7 @@ kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlin kover_gradle_plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" } ksp_gradle_plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } # https://firebase.google.com/docs/android/setup#available-libraries -google_firebase_bom = "com.google.firebase:firebase-bom:34.17.0" +google_firebase_bom = "com.google.firebase:firebase-bom:34.18.0" firebase_appdistribution_gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppDistribution" } autonomousapps_dependencyanalysis_plugin = { module = "com.autonomousapps:dependency-analysis-gradle-plugin", version.ref = "dependencyAnalysis" } ksp_plugin = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } From b8981ff493dc368e92c150b917ae9a93da1eaf42 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 20:01:26 +0200 Subject: [PATCH 111/112] Update dependency io.github.sergio-sastre.ComposablePreviewScanner:android to v0.9.3 (#7535) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b129d1cf27..bb3759fdf7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -175,7 +175,7 @@ test_truth = "com.google.truth:truth:1.4.5" test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.22" test_robolectric = "org.robolectric:robolectric:4.16.1" test_appyx_junit = { module = "com.bumble.appyx:testing-junit4", version.ref = "appyx" } -test_composable_preview_scanner = "io.github.sergio-sastre.ComposablePreviewScanner:android:0.9.2" +test_composable_preview_scanner = "io.github.sergio-sastre.ComposablePreviewScanner:android:0.9.3" test_detekt_api = { module = "io.gitlab.arturbosch.detekt:detekt-api", version.ref = "detekt" } test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version.ref = "detekt" } From c986a2a97106795c4cd4fc84dc55f13823a8efad Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 20:01:45 +0200 Subject: [PATCH 112/112] Update dependency com.google.guava:guava to v33.7.1-android (#7534) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bb3759fdf7..395d5bfc62 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -222,7 +222,7 @@ maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:3.0.2" opusencoder = "io.element.android:opusencoder:1.2.0" zxing_cpp = "io.github.zxing-cpp:android:3.1.1" google_zxing = "com.google.zxing:core:3.5.4" -google_guava = "com.google.guava:guava:33.7.0-android" +google_guava = "com.google.guava:guava:33.7.1-android" haze = { module = "dev.chrisbanes.haze:haze", version.ref = "haze" } haze_materials = { module = "dev.chrisbanes.haze:haze-materials", version.ref = "haze" }