diff --git a/.editorconfig b/.editorconfig index ac30459c21..2ab3cbeae7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -913,3 +913,8 @@ ij_yaml_sequence_on_new_line = false ij_yaml_space_before_colon = false ij_yaml_spaces_within_braces = true ij_yaml_spaces_within_brackets = true + +[**/generated/**] +generated_code = true +ij_formatter_enabled = false +ktlint = disabled \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index 5f13ff4efb..0a1fc8653d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,5 @@ screenshots/**/*.png filter=lfs diff=lfs merge=lfs -text +libraries/compound/screenshots/** filter=lfs diff=lfs merge=lfs -text **/snapshots/**/*.png filter=lfs diff=lfs merge=lfs -text **/docs/images-lfs/*.png filter=lfs diff=lfs merge=lfs -text libraries/mediaupload/impl/src/test/assets/* filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eaea9a841d..fb14c3cee6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,7 +36,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Assemble debug APKs diff --git a/.github/workflows/build_enterprise.yml b/.github/workflows/build_enterprise.yml index 9c4c8cec8f..0d9b5949cc 100644 --- a/.github/workflows/build_enterprise.yml +++ b/.github/workflows/build_enterprise.yml @@ -44,7 +44,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Assemble debug Gplay Enterprise APK diff --git a/.github/workflows/generate_github_pages.yml b/.github/workflows/generate_github_pages.yml index a4286491cb..0d03d1a928 100644 --- a/.github/workflows/generate_github_pages.yml +++ b/.github/workflows/generate_github_pages.yml @@ -19,7 +19,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Set up Python 3.12 diff --git a/.github/workflows/maestro-local.yml b/.github/workflows/maestro-local.yml index 64eff695d9..7481bec0ba 100644 --- a/.github/workflows/maestro-local.yml +++ b/.github/workflows/maestro-local.yml @@ -34,7 +34,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Assemble debug APK diff --git a/.github/workflows/nightlyReports.yml b/.github/workflows/nightlyReports.yml index f89373b44f..537565743e 100644 --- a/.github/workflows/nightlyReports.yml +++ b/.github/workflows/nightlyReports.yml @@ -27,7 +27,7 @@ jobs: java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: false @@ -67,7 +67,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Dependency analysis diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 6e9dea69ab..773bc02d93 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -52,7 +52,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Set up Python 3.12 @@ -90,7 +90,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Run Konsist tests @@ -130,7 +130,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Build Gplay Debug @@ -174,7 +174,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Run Detekt @@ -214,7 +214,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Run Ktlint check @@ -254,7 +254,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Run Knit diff --git a/.github/workflows/recordScreenshots.yml b/.github/workflows/recordScreenshots.yml index a4d54afeb4..bbcbef04d8 100644 --- a/.github/workflows/recordScreenshots.yml +++ b/.github/workflows/recordScreenshots.yml @@ -40,7 +40,7 @@ jobs: java-version: '21' # Add gradle cache, this should speed up the process - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Record screenshots diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 661110338d..2cce85bd5a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 - name: Create app bundle env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} @@ -66,7 +66,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 - name: Create Enterprise app bundle env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} @@ -94,7 +94,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 - name: Create APKs env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} diff --git a/.github/workflows/scripts/recordScreenshots.sh b/.github/workflows/scripts/recordScreenshots.sh index a610c6cc69..19469e69e3 100755 --- a/.github/workflows/scripts/recordScreenshots.sh +++ b/.github/workflows/scripts/recordScreenshots.sh @@ -56,6 +56,12 @@ echo "Deleting previous screenshots" echo "Record screenshots" ./gradlew recordPaparazziDebug --stacktrace $GRADLE_ARGS +echo "Deleting previous screenshots" +./gradlew removeOldScreenshots --stacktrace --warn $GRADLE_ARGS + +echo "Record screenshots (Compound)" +./gradlew :libraries:compound:recordRoborazziDebug --stacktrace -PpreDexEnable=false --max-workers 4 --warn $GRADLE_ARGS + echo "Committing changes" git config http.sslVerify false diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index 080fcef0e0..acfe6ba87f 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -33,7 +33,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Build debug code and test fixtures diff --git a/.github/workflows/sync-localazy.yml b/.github/workflows/sync-localazy.yml index b5548fd2b1..7740af4064 100644 --- a/.github/workflows/sync-localazy.yml +++ b/.github/workflows/sync-localazy.yml @@ -18,7 +18,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Set up Python 3.12 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 55f27aa9ba..22c302cbb3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -52,7 +52,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} @@ -78,6 +78,7 @@ jobs: name: tests-and-screenshot-tests-results path: | **/build/paparazzi/failures/ + **/build/roborazzi/failures/ **/build/reports/tests/*UnitTest/ # https://github.com/codecov/codecov-action diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 254a1fc3cc..3efb2d8dd4 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/.maestro/tests/account/verifySession.yaml b/.maestro/tests/account/verifySession.yaml index f1f4552709..a16322543f 100644 --- a/.maestro/tests/account/verifySession.yaml +++ b/.maestro/tests/account/verifySession.yaml @@ -8,6 +8,6 @@ appId: ${MAESTRO_APP_ID} - hideKeyboard - tapOn: "Continue" - extendedWaitUntil: - visible: "Verification complete" + visible: "Device verified" timeout: 30000 - tapOn: "Continue" diff --git a/CHANGES.md b/CHANGES.md index 5377aedff1..e4d138bfd5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,73 @@ +Changes in Element X v25.09.2 +============================= + +## What's Changed +### ✨ Features +* Show progress dialog while we are sending invites in a room by @richvdh in https://github.com/element-hq/element-x-android/pull/5342 +* Call: RTC decline event support by @BillCarsonFr in https://github.com/element-hq/element-x-android/pull/5305 +* Add room info to the thread's top app bar by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5374 +### 🙌 Improvements +* Use the new RtcNotification event instead of the now deprecated CallNotify by @BillCarsonFr in https://github.com/element-hq/element-x-android/pull/5357 +### 🐛 Bugfixes +* Increase Element Call audio init delay ensuring the right audio device is used by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5315 +* Do not center the dialog title text for dialogs with no icon by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5332 +* Media viewer: release the `ExoPlayers` when the hosting composables are disposed by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5351 +* Make PushData.clientSecret mandatory. by @bmarty in https://github.com/element-hq/element-x-android/pull/5369 +* Cleanup ftue code and ensure verification confirmation is displayed by @bmarty in https://github.com/element-hq/element-x-android/pull/5379 +* Change in clear cache behavior by @bmarty in https://github.com/element-hq/element-x-android/pull/5388 +* fix (room navigation) : fix navigation when leaving room/space by @ganfra in https://github.com/element-hq/element-x-android/pull/5376 +* fix (timeline) : forward pagination regression by @ganfra in https://github.com/element-hq/element-x-android/pull/5389 +* When joining a call, wait for the `content_loaded` action by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5399 +* Ensure the thread summary sender's display name won't wrap to the next line by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5403 +### 🗣 Translations +* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/5349 +* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/5385 +### 🧱 Build +* Improve release script and the file Versions.kt by @bmarty in https://github.com/element-hq/element-x-android/pull/5318 +* Dependency: extract the Matrix SDK and add instructions for upgrading the library by @bmarty in https://github.com/element-hq/element-x-android/pull/5363 +* Add test on DefaultSpaceEntryPoint by @bmarty in https://github.com/element-hq/element-x-android/pull/5343 +### 🚧 In development 🚧 +* Space list by @bmarty in https://github.com/element-hq/element-x-android/pull/5320 +* Feature : Join Space (WIP) by @ganfra in https://github.com/element-hq/element-x-android/pull/5378 +### Dependency upgrades +* Update activity to v1.11.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5324 +* Update dependency com.google.truth:truth to v1.4.5 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5322 +* Update dependency io.sentry:sentry-android to v8.21.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5310 +* Update dependency org.matrix.rustcomponents:sdk-android to v25.9.10 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5323 +* Update dependency androidx.sqlite:sqlite-ktx to v2.6.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5337 +* Update camera to v1.5.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5336 +* Update dependency com.posthog:posthog-android to v3.21.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5333 +* Update dependency com.google.testparameterinjector:test-parameter-injector to v1.19 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5341 +* Upgrade Rust SDK bindings to v25.09.15 by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5353 +* Update dependency org.matrix.rustcomponents:sdk-android to v25.9.16 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5359 +* Update dependency org.matrix.rustcomponents:sdk-android to v25.9.18 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5365 +* Update telephoto to v0.17.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5350 +* Update dependency org.matrix.rustcomponents:sdk-android to v25.9.19 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5377 +* Update dependency com.google.firebase:firebase-bom to v34.3.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5367 +* Upgrade Element Call embedded dependency to `v0.16.0-rc.4` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5391 +* Update dependencyAnalysis to v3 (major) by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5194 +* Update dependency org.maplibre.gl:android-sdk to v11.13.5 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5381 +* Update dependency org.matrix.rustcomponents:sdk-android to v25.9.23 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5396 +* Update plugin dependencycheck to v12.1.5 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5382 +* Update dependency io.sentry:sentry-android to v8.22.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5397 +### Others +* Cleanup nodes by @bmarty in https://github.com/element-hq/element-x-android/pull/5358 +* Complete test on MediaGalleryPresenter by @bmarty in https://github.com/element-hq/element-x-android/pull/5361 +* Remove dead code by @bmarty in https://github.com/element-hq/element-x-android/pull/5306 +* Introduce BugReportFlowNode, and remove NavTarget.ViewLogs from RootFlowNode by @bmarty in https://github.com/element-hq/element-x-android/pull/5370 +* When logging out from Pin code screen, logout from all the sessions. by @bmarty in https://github.com/element-hq/element-x-android/pull/5372 +* Clean MatrixAuthenticationService and SessionStore API by @bmarty in https://github.com/element-hq/element-x-android/pull/5371 +* Add logs to detect duplicates in the room list by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5364 +* Add troubleshoot notification test about blocked users by @bmarty in https://github.com/element-hq/element-x-android/pull/5394 +* Add thread decoration with latest event details by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5355 +* Rework on messages view top bars by @bmarty in https://github.com/element-hq/element-x-android/pull/5401 +* Put developer settings at the end of the view by @p1gp1g in https://github.com/element-hq/element-x-android/pull/5387 + +## New Contributors +* @p1gp1g made their first contribution in https://github.com/element-hq/element-x-android/pull/5387 + +**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v25.09.1...v25.09.2 + Changes in Element X v25.09.1 ============================= diff --git a/annotations/build.gradle.kts b/annotations/build.gradle.kts index 66b88b4dfa..00d0735292 100644 --- a/annotations/build.gradle.kts +++ b/annotations/build.gradle.kts @@ -8,7 +8,3 @@ plugins { alias(libs.plugins.kotlin.jvm) id("com.android.lint") } - -dependencies { - api(libs.inject) -} diff --git a/app/src/main/kotlin/io/element/android/x/di/AppModule.kt b/app/src/main/kotlin/io/element/android/x/di/AppModule.kt index 55d5245735..d98a05321d 100644 --- a/app/src/main/kotlin/io/element/android/x/di/AppModule.kt +++ b/app/src/main/kotlin/io/element/android/x/di/AppModule.kt @@ -33,7 +33,6 @@ import io.element.android.x.BuildConfig import io.element.android.x.R import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.MainScope import kotlinx.coroutines.plus import java.io.File @@ -107,11 +106,7 @@ object AppModule { @Provides @SingleIn(AppScope::class) fun providesCoroutineDispatchers(): CoroutineDispatchers { - return CoroutineDispatchers( - io = Dispatchers.IO, - computation = Dispatchers.Default, - main = Dispatchers.Main, - ) + return CoroutineDispatchers.Default } @Provides diff --git a/app/src/main/kotlin/io/element/android/x/di/DefaultRoomComponentFactory.kt b/app/src/main/kotlin/io/element/android/x/di/DefaultRoomGraphFactory.kt similarity index 84% rename from app/src/main/kotlin/io/element/android/x/di/DefaultRoomComponentFactory.kt rename to app/src/main/kotlin/io/element/android/x/di/DefaultRoomGraphFactory.kt index 8bba1dd50b..9ae8c54eb7 100644 --- a/app/src/main/kotlin/io/element/android/x/di/DefaultRoomComponentFactory.kt +++ b/app/src/main/kotlin/io/element/android/x/di/DefaultRoomGraphFactory.kt @@ -9,15 +9,15 @@ package io.element.android.x.di import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.Inject -import io.element.android.appnav.di.RoomComponentFactory +import io.element.android.appnav.di.RoomGraphFactory import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.room.JoinedRoom @ContributesBinding(SessionScope::class) @Inject -class DefaultRoomComponentFactory( +class DefaultRoomGraphFactory( private val sessionGraph: SessionGraph, -) : RoomComponentFactory { +) : RoomGraphFactory { override fun create(room: JoinedRoom): Any { return sessionGraph.roomGraphFactory .create(room, room) diff --git a/app/src/main/kotlin/io/element/android/x/di/RoomGraph.kt b/app/src/main/kotlin/io/element/android/x/di/RoomGraph.kt index e452bc2a9f..e48dd52daf 100644 --- a/app/src/main/kotlin/io/element/android/x/di/RoomGraph.kt +++ b/app/src/main/kotlin/io/element/android/x/di/RoomGraph.kt @@ -7,18 +7,15 @@ package io.element.android.x.di -import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.GraphExtension import dev.zacsweers.metro.Provides import io.element.android.libraries.architecture.NodeFactoriesBindings import io.element.android.libraries.di.RoomScope -import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.JoinedRoom @GraphExtension(RoomScope::class) interface RoomGraph : NodeFactoriesBindings { - @ContributesTo(SessionScope::class) @GraphExtension.Factory interface Factory { fun create( diff --git a/app/src/main/kotlin/io/element/android/x/di/SessionGraph.kt b/app/src/main/kotlin/io/element/android/x/di/SessionGraph.kt index 255c6c7ab9..3782b00a58 100644 --- a/app/src/main/kotlin/io/element/android/x/di/SessionGraph.kt +++ b/app/src/main/kotlin/io/element/android/x/di/SessionGraph.kt @@ -7,8 +7,6 @@ package io.element.android.x.di -import dev.zacsweers.metro.AppScope -import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.GraphExtension import dev.zacsweers.metro.Provides import io.element.android.libraries.architecture.NodeFactoriesBindings @@ -19,7 +17,6 @@ import io.element.android.libraries.matrix.api.MatrixClient interface SessionGraph : NodeFactoriesBindings { val roomGraphFactory: RoomGraph.Factory - @ContributesTo(AppScope::class) @GraphExtension.Factory interface Factory { fun create(@Provides matrixClient: MatrixClient): SessionGraph diff --git a/appnav/build.gradle.kts b/appnav/build.gradle.kts index 3ea6f9d1c9..063e26b9da 100644 --- a/appnav/build.gradle.kts +++ b/appnav/build.gradle.kts @@ -26,9 +26,11 @@ dependencies { allFeaturesApi(project) implementation(projects.libraries.core) + implementation(projects.libraries.accountselect.api) implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) implementation(projects.libraries.deeplink.api) + implementation(projects.libraries.featureflag.api) implementation(projects.libraries.matrix.api) implementation(projects.libraries.oidc.api) implementation(projects.libraries.preferences.api) @@ -36,11 +38,13 @@ dependencies { implementation(projects.libraries.pushproviders.api) implementation(projects.libraries.designsystem) implementation(projects.libraries.matrixui) + implementation(projects.libraries.uiCommon) implementation(projects.libraries.uiStrings) implementation(projects.features.login.api) implementation(libs.coil) + implementation(projects.features.announcement.api) implementation(projects.features.ftue.api) implementation(projects.features.share.api) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt index ef3d4b27b9..290a351e86 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt @@ -24,7 +24,7 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.appnav.di.SessionGraphFactory import io.element.android.libraries.architecture.NodeInputs @@ -41,7 +41,7 @@ import kotlinx.parcelize.Parcelize * This allow to inject objects with SessionScope in the constructor of [LoggedInFlowNode]. */ @ContributesNode(AppScope::class) -@Inject +@AssistedInject class LoggedInAppScopeFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, @@ -57,6 +57,7 @@ class LoggedInAppScopeFlowNode( ), DependencyInjectionGraphOwner { interface Callback : Plugin { fun onOpenBugReport() + fun onAddAccount() } @Parcelize @@ -83,6 +84,10 @@ class LoggedInAppScopeFlowNode( override fun onOpenBugReport() { plugins().forEach { it.onOpenBugReport() } } + + override fun onAddAccount() { + plugins().forEach { it.onAddAccount() } + } } return createNode(buildContext, listOf(callback)) } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt index 5444b5c465..c557d6e1c2 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt @@ -31,9 +31,17 @@ class LoggedInEventProcessor( observingJob = roomMembershipObserver.updates .filter { !it.isUserInRoom } .distinctUntilChanged() - .onEach { - when (it.change) { - MembershipChange.LEFT -> displayMessage(CommonStrings.common_current_user_left_room) + .onEach { roomMemberShipUpdate -> + when (roomMemberShipUpdate.change) { + MembershipChange.LEFT -> { + displayMessage( + if (roomMemberShipUpdate.isSpace) { + CommonStrings.common_current_user_left_space + } else { + CommonStrings.common_current_user_left_room + } + ) + } MembershipChange.INVITATION_REJECTED -> displayMessage(CommonStrings.common_current_user_rejected_invite) MembershipChange.KNOCK_RETRACTED -> displayMessage(CommonStrings.common_current_user_canceled_knock) else -> Unit diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index fc4fcc3a7a..b0fc707d07 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -36,9 +36,8 @@ import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import com.bumble.appyx.navmodel.backstack.operation.replace import com.bumble.appyx.navmodel.backstack.operation.singleTop -import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.annotations.ContributesNode import io.element.android.appnav.loggedin.LoggedInNode @@ -75,13 +74,13 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.MAIN_SPACE import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomIdOrAlias -import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.verification.SessionVerificationServiceListener import io.element.android.libraries.matrix.api.verification.VerificationRequest import io.element.android.libraries.push.api.notifications.conversations.NotificationConversationService +import io.element.android.libraries.ui.common.nodes.emptyNode import io.element.android.services.appnavstate.api.AppNavigationStateService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.first @@ -100,7 +99,7 @@ import kotlin.time.Duration.Companion.seconds import kotlin.time.toKotlinDuration @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class LoggedInFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, @@ -138,11 +137,12 @@ class LoggedInFlowNode( ) { interface Callback : Plugin { fun onOpenBugReport() + fun onAddAccount() } private val loggedInFlowProcessor = LoggedInEventProcessor( - snackbarDispatcher, - matrixClient.roomMembershipObserver(), + snackbarDispatcher = snackbarDispatcher, + roomMembershipObserver = matrixClient.roomMembershipObserver, ) private val verificationListener = object : SessionVerificationServiceListener { @@ -189,7 +189,7 @@ class LoggedInFlowNode( // TODO We do not support Space yet, so directly navigate to main space appNavigationStateService.onNavigateToSpace(id, MAIN_SPACE) loggedInFlowProcessor.observeEvents(sessionCoroutineScope) - matrixClient.sessionVerificationService().setListener(verificationListener) + matrixClient.sessionVerificationService.setListener(verificationListener) mediaPreviewConfigMigration() sessionCoroutineScope.launch { @@ -218,7 +218,7 @@ class LoggedInFlowNode( appNavigationStateService.onLeavingSpace(id) appNavigationStateService.onLeavingSession(id) loggedInFlowProcessor.stopObserving() - matrixClient.sessionVerificationService().setListener(null) + matrixClient.sessionVerificationService.setListener(null) } ) setupSendingQueue() @@ -281,7 +281,7 @@ class LoggedInFlowNode( override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { - NavTarget.Placeholder -> createNode(buildContext) + NavTarget.Placeholder -> emptyNode(buildContext) NavTarget.LoggedInPermanent -> { val callback = object : LoggedInNode.Callback { override fun navigateToNotificationTroubleshoot() { @@ -366,8 +366,8 @@ class LoggedInFlowNode( } } val spaceCallback = object : SpaceEntryPoint.Callback { - override fun onOpenRoom(roomId: RoomId) { - backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias())) + override fun onOpenRoom(roomId: RoomId, viaParameters: List) { + backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), serverNames = viaParameters)) } } val inputs = RoomFlowNode.Inputs( @@ -392,6 +392,10 @@ class LoggedInFlowNode( } is NavTarget.Settings -> { val callback = object : PreferencesEntryPoint.Callback { + override fun onAddAccount() { + plugins().forEach { it.onAddAccount() } + } + override fun onOpenBugReport() { plugins().forEach { it.onOpenBugReport() } } @@ -404,11 +408,7 @@ class LoggedInFlowNode( backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.NotificationSettings)) } - override fun navigateTo(sessionId: SessionId, roomId: RoomId, eventId: EventId) { - // We do not check the sessionId, but it will have to be done at some point (multi account) - if (sessionId != matrixClient.sessionId) { - Timber.e("SessionId mismatch, expected ${matrixClient.sessionId} but got $sessionId") - } + override fun navigateTo(roomId: RoomId, eventId: EventId) { backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Messages(eventId))) } } @@ -548,13 +548,6 @@ class LoggedInFlowNode( } } -@ContributesNode(AppScope::class) -@Inject -class PlaceholderNode( - @Assisted buildContext: BuildContext, - @Assisted plugins: List, -) : Node(buildContext, plugins = plugins) - @Parcelize private class AttachRoomOperation( val roomTarget: LoggedInFlowNode.NavTarget.Room, diff --git a/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt index db33ee873e..5da44f715d 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt @@ -22,7 +22,7 @@ import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.api.LoginEntryPoint import io.element.android.features.login.api.LoginParams @@ -36,7 +36,7 @@ import io.element.android.libraries.matrix.ui.media.NotLoggedInImageLoaderFactor import kotlinx.parcelize.Parcelize @ContributesNode(AppScope::class) -@Inject +@AssistedInject class NotLoggedInFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt index 3f1d40335d..19290c5f8b 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt @@ -9,23 +9,23 @@ package io.element.android.appnav import android.content.Intent import android.os.Parcelable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.lifecycle.lifecycleScope import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.node.node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.state.MutableSavedStateMap import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push +import com.bumble.appyx.navmodel.backstack.transitionhandler.rememberBackstackFader +import com.bumble.appyx.navmodel.backstack.transitionhandler.rememberBackstackSlider import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.annotations.ContributesNode import io.element.android.appnav.di.MatrixSessionCache @@ -34,18 +34,22 @@ import io.element.android.appnav.intent.ResolvedIntent import io.element.android.appnav.root.RootNavStateFlowFactory import io.element.android.appnav.root.RootPresenter import io.element.android.appnav.root.RootView +import io.element.android.features.announcement.api.AnnouncementService import io.element.android.features.login.api.LoginParams import io.element.android.features.login.api.accesscontrol.AccountProviderAccessControl import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.features.signedout.api.SignedOutEntryPoint +import io.element.android.libraries.accountselect.api.AccountSelectEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.appyx.rememberDelegateTransitionHandler import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.waitForChildAttached import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.deeplink.api.DeeplinkData -import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.permalink.PermalinkData @@ -53,14 +57,16 @@ import io.element.android.libraries.oidc.api.OidcAction import io.element.android.libraries.oidc.api.OidcActionFlow import io.element.android.libraries.sessionstorage.api.LoggedInState import io.element.android.libraries.sessionstorage.api.SessionStore +import io.element.android.libraries.ui.common.nodes.emptyNode import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import timber.log.Timber @ContributesNode(AppScope::class) -@Inject +@AssistedInject class RootFlowNode( @Assisted val buildContext: BuildContext, @Assisted plugins: List, @@ -71,9 +77,12 @@ class RootFlowNode( private val presenter: RootPresenter, private val bugReportEntryPoint: BugReportEntryPoint, private val signedOutEntryPoint: SignedOutEntryPoint, + private val accountSelectEntryPoint: AccountSelectEntryPoint, private val intentResolver: IntentResolver, private val oidcActionFlow: OidcActionFlow, private val bugReporter: BugReporter, + private val featureFlagService: FeatureFlagService, + private val announcementService: AnnouncementService, ) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.SplashScreen, @@ -95,27 +104,24 @@ class RootFlowNode( } private fun observeNavState() { - navStateFlowFactory.create(buildContext.savedStateMap) - .distinctUntilChanged() - .onEach { navState -> - Timber.v("navState=$navState") - when (navState.loggedInState) { - is LoggedInState.LoggedIn -> { - if (navState.loggedInState.isTokenValid) { - tryToRestoreLatestSession( - onSuccess = { sessionId -> switchToLoggedInFlow(sessionId, navState.cacheIndex) }, - onFailure = { switchToNotLoggedInFlow(null) } - ) - } else { - switchToSignedOutFlow(SessionId(navState.loggedInState.sessionId)) - } - } - LoggedInState.NotLoggedIn -> { - switchToNotLoggedInFlow(null) + navStateFlowFactory.create(buildContext.savedStateMap).distinctUntilChanged().onEach { navState -> + Timber.v("navState=$navState") + when (navState.loggedInState) { + is LoggedInState.LoggedIn -> { + if (navState.loggedInState.isTokenValid) { + tryToRestoreLatestSession( + onSuccess = { sessionId -> switchToLoggedInFlow(sessionId, navState.cacheIndex) }, + onFailure = { switchToNotLoggedInFlow(null) } + ) + } else { + switchToSignedOutFlow(SessionId(navState.loggedInState.sessionId)) } } + LoggedInState.NotLoggedIn -> { + switchToNotLoggedInFlow(null) + } } - .launchIn(lifecycleScope) + }.launchIn(lifecycleScope) } private fun switchToLoggedInFlow(sessionId: SessionId, navId: Int) { @@ -137,20 +143,17 @@ class RootFlowNode( onFailure: () -> Unit, onSuccess: (SessionId) -> Unit, ) { - matrixSessionCache.getOrRestore(sessionId) - .onSuccess { - Timber.v("Succeed to restore session $sessionId") - onSuccess(sessionId) - } - .onFailure { - Timber.e(it, "Failed to restore session $sessionId") - onFailure() - } + matrixSessionCache.getOrRestore(sessionId).onSuccess { + Timber.v("Succeed to restore session $sessionId") + onSuccess(sessionId) + }.onFailure { + Timber.e(it, "Failed to restore session $sessionId") + onFailure() + } } private suspend fun tryToRestoreLatestSession( - onSuccess: (SessionId) -> Unit, - onFailure: () -> Unit + onSuccess: (SessionId) -> Unit, onFailure: () -> Unit ) { val latestSessionId = sessionStore.getLatestSessionId() if (latestSessionId == null) { @@ -172,45 +175,64 @@ class RootFlowNode( modifier = modifier, onOpenBugReport = this::onOpenBugReport, ) { - BackstackView() + val backstackSlider = rememberBackstackSlider( + transitionSpec = { spring(stiffness = Spring.StiffnessMediumLow) }, + ) + val backstackFader = rememberBackstackFader( + transitionSpec = { spring(stiffness = Spring.StiffnessMediumLow) }, + ) + val transitionHandler = rememberDelegateTransitionHandler { navTarget -> + when (navTarget) { + is NavTarget.SplashScreen, + is NavTarget.LoggedInFlow -> backstackFader + else -> backstackSlider + } + } + BackstackView(transitionHandler = transitionHandler) + announcementService.Render(Modifier) } } sealed interface NavTarget : Parcelable { - @Parcelize - data object SplashScreen : NavTarget + @Parcelize data object SplashScreen : NavTarget - @Parcelize - data class NotLoggedInFlow( + @Parcelize data class AccountSelect( + val currentSessionId: SessionId, + val intent: Intent?, + val permalinkData: PermalinkData?, + ) : NavTarget + + @Parcelize data class NotLoggedInFlow( val params: LoginParams? ) : NavTarget - @Parcelize - data class LoggedInFlow( - val sessionId: SessionId, - val navId: Int + @Parcelize data class LoggedInFlow( + val sessionId: SessionId, val navId: Int ) : NavTarget - @Parcelize - data class SignedOutFlow( + @Parcelize data class SignedOutFlow( val sessionId: SessionId ) : NavTarget - @Parcelize - data object BugReport : NavTarget + @Parcelize data object BugReport : NavTarget } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { is NavTarget.LoggedInFlow -> { - val matrixClient = matrixSessionCache.getOrNull(navTarget.sessionId) ?: return splashNode(buildContext).also { - Timber.w("Couldn't find any session, go through SplashScreen") - } + val matrixClient = matrixSessionCache.getOrNull(navTarget.sessionId) + ?: return emptyNode(buildContext).also { + Timber.w("Couldn't find any session, go through SplashScreen") + } val inputs = LoggedInAppScopeFlowNode.Inputs(matrixClient) val callback = object : LoggedInAppScopeFlowNode.Callback { override fun onOpenBugReport() { backstack.push(NavTarget.BugReport) } + + override fun onAddAccount() { + backstack.push(NavTarget.NotLoggedInFlow(null)) + } } createNode(buildContext, plugins = listOf(inputs, callback)) } @@ -226,32 +248,46 @@ class RootFlowNode( createNode(buildContext, plugins = listOf(params, callback)) } is NavTarget.SignedOutFlow -> { - signedOutEntryPoint.nodeBuilder(this, buildContext) - .params( - SignedOutEntryPoint.Params( - sessionId = navTarget.sessionId - ) + signedOutEntryPoint.nodeBuilder(this, buildContext).params( + SignedOutEntryPoint.Params( + sessionId = navTarget.sessionId ) - .build() + ).build() } - NavTarget.SplashScreen -> splashNode(buildContext) + NavTarget.SplashScreen -> emptyNode(buildContext) NavTarget.BugReport -> { val callback = object : BugReportEntryPoint.Callback { override fun onDone() { backstack.pop() } } - bugReportEntryPoint - .nodeBuilder(this, buildContext) - .callback(callback) - .build() + bugReportEntryPoint.nodeBuilder(this, buildContext).callback(callback).build() } - } - } + is NavTarget.AccountSelect -> { + val callback: AccountSelectEntryPoint.Callback = object : AccountSelectEntryPoint.Callback { + override fun onSelectAccount(sessionId: SessionId) { + lifecycleScope.launch { + if (sessionId == navTarget.currentSessionId) { + // Ensure that the account selection Node is removed from the backstack + // Do not pop when the account is changed to avoid a UI flicker. + backstack.pop() + } + attachSession(sessionId).apply { + if (navTarget.intent != null) { + attachIncomingShare(navTarget.intent) + } else if (navTarget.permalinkData != null) { + attachPermalinkData(navTarget.permalinkData) + } + } + } + } - private fun splashNode(buildContext: BuildContext) = node(buildContext) { - Box(modifier = it.fillMaxSize(), contentAlignment = Alignment.Center) { - CircularProgressIndicator() + override fun onCancel() { + backstack.pop() + } + } + accountSelectEntryPoint.nodeBuilder(this, buildContext).callback(callback).build() + } } } @@ -267,19 +303,29 @@ class RootFlowNode( } private suspend fun onLoginLink(params: LoginParams) { - // Is there a session already? - val latestSessionId = sessionStore.getLatestSessionId() - if (latestSessionId == null) { - // No session, open login - if (accountProviderAccessControl.isAllowedToConnectToAccountProvider(params.accountProvider.ensureProtocol())) { - switchToNotLoggedInFlow(params) + if (accountProviderAccessControl.isAllowedToConnectToAccountProvider(params.accountProvider.ensureProtocol())) { + // Is there a session already? + val sessions = sessionStore.getAllSessions() + if (sessions.isNotEmpty()) { + if (featureFlagService.isFeatureEnabled(FeatureFlags.MultiAccount)) { + val loginHintMatrixId = params.loginHint?.removePrefix("mxid:") + val existingAccount = sessions.find { it.userId == loginHintMatrixId } + if (existingAccount != null) { + // We have an existing account matching the login hint, ensure this is the current session + sessionStore.setLatestSession(existingAccount.userId) + } else { + val latestSessionId = sessions.maxBy { it.lastUsageIndex }.userId + attachSession(SessionId(latestSessionId)) + backstack.push(NavTarget.NotLoggedInFlow(params)) + } + } else { + Timber.w("Login link ignored, multi account is disabled") + } } else { - Timber.w("Login link ignored, we are not allowed to connect to the homeserver") - switchToNotLoggedInFlow(null) + switchToNotLoggedInFlow(params) } } else { - // Just ignore the login link if we already have a session - Timber.w("Login link ignored, we already have a session") + Timber.w("Login link ignored, we are not allowed to connect to the homeserver") } } @@ -290,56 +336,95 @@ class RootFlowNode( // No session, open login switchToNotLoggedInFlow(null) } else { - attachSession(latestSessionId) - .attachIncomingShare(intent) + // wait for the current session to be restored + val loggedInFlowNode = attachSession(latestSessionId) + if (sessionStore.getAllSessions().size > 1) { + // Several accounts, let the user choose which one to use + backstack.push( + NavTarget.AccountSelect( + currentSessionId = latestSessionId, + intent = intent, + permalinkData = null, + ) + ) + } else { + // Only one account, directly attach the incoming share node. + loggedInFlowNode.attachIncomingShare(intent) + } } } private suspend fun navigateTo(permalinkData: PermalinkData) { Timber.d("Navigating to $permalinkData") - attachSession(null) - .apply { - when (permalinkData) { - is PermalinkData.FallbackLink -> Unit - is PermalinkData.RoomEmailInviteLink -> Unit - is PermalinkData.RoomLink -> { - attachRoom( - roomIdOrAlias = permalinkData.roomIdOrAlias, - trigger = JoinedRoom.Trigger.MobilePermalink, - serverNames = permalinkData.viaParameters, - eventId = permalinkData.eventId, - clearBackstack = true + // Is there a session already? + val latestSessionId = sessionStore.getLatestSessionId() + if (latestSessionId == null) { + // No session, open login + switchToNotLoggedInFlow(null) + } else { + // wait for the current session to be restored + val loggedInFlowNode = attachSession(latestSessionId) + when (permalinkData) { + is PermalinkData.FallbackLink -> Unit + is PermalinkData.RoomEmailInviteLink -> Unit + else -> { + if (sessionStore.getAllSessions().size > 1) { + // Several accounts, let the user choose which one to use + backstack.push( + NavTarget.AccountSelect( + currentSessionId = latestSessionId, + intent = null, + permalinkData = permalinkData, + ) ) - } - is PermalinkData.UserLink -> { - attachUser(permalinkData.userId) + } else { + // Only one account, directly attach the room or the user node. + loggedInFlowNode.attachPermalinkData(permalinkData) } } } + } + } + + private suspend fun LoggedInFlowNode.attachPermalinkData(permalinkData: PermalinkData) { + when (permalinkData) { + is PermalinkData.FallbackLink -> Unit + is PermalinkData.RoomEmailInviteLink -> Unit + is PermalinkData.RoomLink -> { + attachRoom( + roomIdOrAlias = permalinkData.roomIdOrAlias, + trigger = JoinedRoom.Trigger.MobilePermalink, + serverNames = permalinkData.viaParameters, + eventId = permalinkData.eventId, + clearBackstack = true + ) + } + is PermalinkData.UserLink -> { + attachUser(permalinkData.userId) + } + } } private suspend fun navigateTo(deeplinkData: DeeplinkData) { Timber.d("Navigating to $deeplinkData") - attachSession(deeplinkData.sessionId) - .apply { - when (deeplinkData) { - is DeeplinkData.Root -> Unit // The room list will always be shown, observing FtueState - is DeeplinkData.Room -> attachRoom(deeplinkData.roomId.toRoomIdOrAlias(), clearBackstack = true) - } + attachSession(deeplinkData.sessionId).apply { + when (deeplinkData) { + is DeeplinkData.Root -> Unit // The room list will always be shown, observing FtueState + is DeeplinkData.Room -> attachRoom(deeplinkData.roomId.toRoomIdOrAlias(), clearBackstack = true) } + } } private fun onOidcAction(oidcAction: OidcAction) { oidcActionFlow.post(oidcAction) } - // [sessionId] will be null for permalink. - private suspend fun attachSession(sessionId: SessionId?): LoggedInFlowNode { - // TODO handle multi-session + private suspend fun attachSession(sessionId: SessionId): LoggedInFlowNode { + // Ensure that the session is the latest one + sessionStore.setLatestSession(sessionId.value) return waitForChildAttached { navTarget -> - navTarget is NavTarget.LoggedInFlow && (sessionId == null || navTarget.sessionId == sessionId) - } - .attachSession() + navTarget is NavTarget.LoggedInFlow && navTarget.sessionId == sessionId + }.attachSession() } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/di/MatrixSessionCache.kt b/appnav/src/main/kotlin/io/element/android/appnav/di/MatrixSessionCache.kt index 78b9617eae..ce80ac7207 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/di/MatrixSessionCache.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/di/MatrixSessionCache.kt @@ -109,7 +109,10 @@ class MatrixSessionCache( } private fun onNewMatrixClient(matrixClient: MatrixClient) { - val syncOrchestrator = syncOrchestratorFactory.create(matrixClient) + val syncOrchestrator = syncOrchestratorFactory.create( + syncService = matrixClient.syncService, + sessionCoroutineScope = matrixClient.sessionCoroutineScope, + ) sessionIdsToMatrixSession[matrixClient.sessionId] = InMemoryMatrixSession( matrixClient = matrixClient, syncOrchestrator = syncOrchestrator, diff --git a/appnav/src/main/kotlin/io/element/android/appnav/di/RoomComponentFactory.kt b/appnav/src/main/kotlin/io/element/android/appnav/di/RoomGraphFactory.kt similarity index 90% rename from appnav/src/main/kotlin/io/element/android/appnav/di/RoomComponentFactory.kt rename to appnav/src/main/kotlin/io/element/android/appnav/di/RoomGraphFactory.kt index fc8f2c175b..ae0a3a81f2 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/di/RoomComponentFactory.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/di/RoomGraphFactory.kt @@ -9,6 +9,6 @@ package io.element.android.appnav.di import io.element.android.libraries.matrix.api.room.JoinedRoom -fun interface RoomComponentFactory { +fun interface RoomGraphFactory { fun create(room: JoinedRoom): Any } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/di/SyncOrchestrator.kt b/appnav/src/main/kotlin/io/element/android/appnav/di/SyncOrchestrator.kt index 26377acae8..2561fd3ac7 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/di/SyncOrchestrator.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/di/SyncOrchestrator.kt @@ -10,14 +10,15 @@ package io.element.android.appnav.di import androidx.annotation.VisibleForTesting import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.childScope -import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.services.appnavstate.api.AppForegroundStateService +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce @@ -30,23 +31,25 @@ import java.util.concurrent.atomic.AtomicBoolean import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds -@Inject +@AssistedInject class SyncOrchestrator( - @Assisted matrixClient: MatrixClient, + @Assisted private val syncService: SyncService, + @Assisted sessionCoroutineScope: CoroutineScope, private val appForegroundStateService: AppForegroundStateService, private val networkMonitor: NetworkMonitor, dispatchers: CoroutineDispatchers, ) { @AssistedFactory interface Factory { - fun create(matrixClient: MatrixClient): SyncOrchestrator + fun create( + syncService: SyncService, + sessionCoroutineScope: CoroutineScope, + ): SyncOrchestrator } - private val syncService = matrixClient.syncService() - private val tag = "SyncOrchestrator" - private val coroutineScope = matrixClient.sessionCoroutineScope.childScope(dispatchers.io, tag) + private val coroutineScope = sessionCoroutineScope.childScope(dispatchers.io, tag) private val started = AtomicBoolean(false) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInNode.kt index 05c721360f..edc2be05db 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInNode.kt @@ -14,12 +14,12 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class LoggedInNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt index 6f04a08f7b..9a84049497 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt @@ -22,7 +22,7 @@ import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.newRoot import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.annotations.ContributesNode import io.element.android.appnav.room.joined.JoinedRoomFlowNode @@ -64,7 +64,7 @@ import java.util.Optional import kotlin.jvm.optionals.getOrNull @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class RoomFlowNode( @Assisted val buildContext: BuildContext, @Assisted plugins: List, diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomFlowNode.kt index 62b10e7a80..cbda7a8bfb 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomFlowNode.kt @@ -25,7 +25,7 @@ import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.newRoot import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.appnav.room.RoomNavigationTarget import io.element.android.libraries.architecture.BackstackView @@ -45,7 +45,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class JoinedRoomFlowNode( @Assisted val buildContext: BuildContext, @Assisted plugins: List, diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt index b36a24d4f0..f0f8dff3e7 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt @@ -18,9 +18,9 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode -import io.element.android.appnav.di.RoomComponentFactory +import io.element.android.appnav.di.RoomGraphFactory import io.element.android.appnav.room.RoomNavigationTarget import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint @@ -45,7 +45,7 @@ import kotlinx.parcelize.Parcelize import timber.log.Timber @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class JoinedRoomLoadedFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, @@ -56,7 +56,7 @@ class JoinedRoomLoadedFlowNode( private val sessionCoroutineScope: CoroutineScope, private val matrixClient: MatrixClient, private val activeRoomsHolder: ActiveRoomsHolder, - roomComponentFactory: RoomComponentFactory, + roomGraphFactory: RoomGraphFactory, ) : BaseFlowNode( backstack = BackStack( initialElement = when (val input = plugins.filterIsInstance().first().initialElement) { @@ -83,7 +83,7 @@ class JoinedRoomLoadedFlowNode( private val inputs: Inputs = inputs() private val callbacks = plugins.filterIsInstance() - override val graph = roomComponentFactory.create(inputs.room) + override val graph = roomGraphFactory.create(inputs.room) init { lifecycle.subscribe( diff --git a/appnav/src/test/kotlin/io/element/android/appnav/JoinedRoomLoadedFlowNodeTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/JoinedRoomLoadedFlowNodeTest.kt index bbaa196520..dfb2638dc1 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/JoinedRoomLoadedFlowNodeTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/JoinedRoomLoadedFlowNodeTest.kt @@ -17,7 +17,7 @@ import com.bumble.appyx.navmodel.backstack.activeElement import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.bumble.appyx.testing.unit.common.helper.parentNodeTestHelper import com.google.common.truth.Truth.assertThat -import io.element.android.appnav.di.RoomComponentFactory +import io.element.android.appnav.di.RoomGraphFactory import io.element.android.appnav.room.RoomNavigationTarget import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode import io.element.android.features.messages.api.MessagesEntryPoint @@ -70,7 +70,7 @@ class JoinedRoomLoadedFlowNodeTest { } } - private class FakeRoomComponentFactory : RoomComponentFactory { + private class FakeRoomGraphFactory : RoomGraphFactory { override fun create(room: JoinedRoom): Any { return Unit } @@ -110,7 +110,7 @@ class JoinedRoomLoadedFlowNodeTest { roomDetailsEntryPoint = roomDetailsEntryPoint, appNavigationStateService = FakeAppNavigationStateService(), sessionCoroutineScope = this, - roomComponentFactory = FakeRoomComponentFactory(), + roomGraphFactory = FakeRoomGraphFactory(), matrixClient = FakeMatrixClient(), activeRoomsHolder = activeRoomsHolder, ) diff --git a/appnav/src/test/kotlin/io/element/android/appnav/SyncOrchestratorTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/SyncOrchestratorTest.kt index 2c143df095..f26df83a56 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/SyncOrchestratorTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/SyncOrchestratorTest.kt @@ -11,7 +11,6 @@ import io.element.android.appnav.di.SyncOrchestrator import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.libraries.matrix.api.sync.SyncState -import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.sync.FakeSyncService import io.element.android.services.appnavstate.test.FakeAppForegroundStateService import io.element.android.tests.testutils.WarmUpRule @@ -385,7 +384,8 @@ class SyncOrchestratorTest { networkMonitor: FakeNetworkMonitor = FakeNetworkMonitor(), appForegroundStateService: FakeAppForegroundStateService = FakeAppForegroundStateService(), ) = SyncOrchestrator( - matrixClient = FakeMatrixClient(syncService = syncService, sessionCoroutineScope = backgroundScope), + syncService = syncService, + sessionCoroutineScope = backgroundScope, networkMonitor = networkMonitor, appForegroundStateService = appForegroundStateService, dispatchers = testCoroutineDispatchers(), diff --git a/appnav/src/test/kotlin/io/element/android/appnav/di/MatrixSessionCacheTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/di/MatrixSessionCacheTest.kt index 47237d5f77..6df80ee95d 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/di/MatrixSessionCacheTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/di/MatrixSessionCacheTest.kt @@ -10,12 +10,13 @@ package io.element.android.appnav.di import com.bumble.appyx.core.state.MutableSavedStateMapImpl import com.google.common.truth.Truth.assertThat import io.element.android.features.networkmonitor.test.FakeNetworkMonitor -import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.services.appnavstate.test.FakeAppForegroundStateService import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test @@ -117,9 +118,13 @@ class MatrixSessionCacheTest { } private fun TestScope.createSyncOrchestratorFactory() = object : SyncOrchestrator.Factory { - override fun create(matrixClient: MatrixClient): SyncOrchestrator { + override fun create( + syncService: SyncService, + sessionCoroutineScope: CoroutineScope, + ): SyncOrchestrator { return SyncOrchestrator( - matrixClient, + syncService = syncService, + sessionCoroutineScope = sessionCoroutineScope, appForegroundStateService = FakeAppForegroundStateService(), networkMonitor = FakeNetworkMonitor(), dispatchers = testCoroutineDispatchers(), diff --git a/appnav/src/test/kotlin/io/element/android/appnav/intent/IntentResolverTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/intent/IntentResolverTest.kt index 05898c75f3..def1f33253 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/intent/IntentResolverTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/intent/IntentResolverTest.kt @@ -111,7 +111,7 @@ class IntentResolverTest { @Test fun `test resolve oidc`() { val sut = createIntentResolver( - oidcIntentResolverResult = { OidcAction.GoBack }, + oidcIntentResolverResult = { OidcAction.GoBack() }, ) val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { action = Intent.ACTION_VIEW @@ -120,7 +120,7 @@ class IntentResolverTest { val result = sut.resolve(intent) assertThat(result).isEqualTo( ResolvedIntent.Oidc( - oidcAction = OidcAction.GoBack + oidcAction = OidcAction.GoBack() ) ) } diff --git a/build.gradle.kts b/build.gradle.kts index 7171d5b079..b7662aeb56 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,6 +15,7 @@ plugins { alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.ksp) apply false alias(libs.plugins.dependencycheck) apply false + alias(libs.plugins.roborazzi) apply false alias(libs.plugins.dependencyanalysis) alias(libs.plugins.detekt) alias(libs.plugins.ktlint) @@ -192,6 +193,21 @@ subprojects { tasks.findByName("recordPaparazziRelease")?.dependsOn(removeOldScreenshotsTask) } +// Make sure to delete old snapshot before recording new ones +subprojects { + val screenshotsDir = File("${project.projectDir}/screenshots") + val removeOldScreenshotsTask = tasks.register("removeOldScreenshots") { + onlyIf { screenshotsDir.exists() } + doFirst { + println("Delete previous screenshots located at $screenshotsDir\n") + screenshotsDir.deleteRecursively() + } + } + tasks.findByName("recordRoborazzi")?.dependsOn(removeOldScreenshotsTask) + tasks.findByName("recordRoborazziDebug")?.dependsOn(removeOldScreenshotsTask) + tasks.findByName("recordRoborazziRelease")?.dependsOn(removeOldScreenshotsTask) +} + subprojects { tasks.withType().configureEach { compilerOptions { diff --git a/codegen/src/main/kotlin/io/element/android/codegen/ContributesNodeProcessor.kt b/codegen/src/main/kotlin/io/element/android/codegen/ContributesNodeProcessor.kt index f50a3b6fac..15bb4afbe0 100644 --- a/codegen/src/main/kotlin/io/element/android/codegen/ContributesNodeProcessor.kt +++ b/codegen/src/main/kotlin/io/element/android/codegen/ContributesNodeProcessor.kt @@ -35,6 +35,7 @@ import dev.zacsweers.metro.BindingContainer import dev.zacsweers.metro.Binds import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.IntoMap +import dev.zacsweers.metro.Origin import io.element.android.annotations.ContributesNode import org.jetbrains.kotlin.name.FqName @@ -71,14 +72,16 @@ class ContributesNodeProcessor( val scope = annotation.arguments.find { it.name?.asString() == "scope" }!!.value as KSType val modulePackage = ksClass.packageName.asString() val moduleClassName = "${ksClass.simpleName.asString()}_Module" + val nodeClassName = ClassName.bestGuess(ksClass.qualifiedName!!.asString()) val content = FileSpec.builder( packageName = modulePackage, fileName = moduleClassName, ) .addType( TypeSpec.interfaceBuilder(moduleClassName) + .addAnnotation(AnnotationSpec.builder(Origin::class).addMember(CLASS_PLACEHOLDER, nodeClassName).build()) .addAnnotation(BindingContainer::class) - .addAnnotation(AnnotationSpec.builder(ContributesTo::class).addMember("%T::class", scope.toTypeName()).build()) + .addAnnotation(AnnotationSpec.builder(ContributesTo::class).addMember(CLASS_PLACEHOLDER, scope.toTypeName()).build()) .addFunction( FunSpec.builder("bind${ksClass.simpleName.asString()}Factory") .addModifiers(KModifier.ABSTRACT) @@ -88,7 +91,7 @@ class ContributesNodeProcessor( .addAnnotation(IntoMap::class) .addAnnotation( AnnotationSpec.Companion.builder(ClassName.bestGuess(nodeKeyFqName.asString())).addMember( - "%T::class", + CLASS_PLACEHOLDER, ClassName.bestGuess(ksClass.qualifiedName!!.asString()) ).build() ) @@ -115,7 +118,7 @@ class ContributesNodeProcessor( val assistedParameters = constructor.parameters.filter { it.isAnnotationPresent(Assisted::class) } if (assistedParameters.size != 2) { error( - "${ksClass.qualifiedName?.asString()} must have an @Inject constructor with 2 @Assisted parameters. Found: ${assistedParameters.size}", + "${ksClass.qualifiedName?.asString()} must have a constructor with 2 @Assisted parameters. Found: ${assistedParameters.size}", ) } val contextAssistedParam = assistedParameters[0] @@ -138,6 +141,7 @@ class ContributesNodeProcessor( .addType( TypeSpec.interfaceBuilder(assistedFactoryClassName) .addSuperinterface(ClassName.bestGuess(assistedNodeFactoryFqName.asString()).parameterizedBy(nodeClassName)) + .addAnnotation(AnnotationSpec.builder(Origin::class).addMember("%T::class", nodeClassName).build()) .addAnnotation(AssistedFactory::class) .addFunction( FunSpec.builder("create") @@ -161,6 +165,7 @@ class ContributesNodeProcessor( } companion object { + private const val CLASS_PLACEHOLDER = "%T::class" private val assistedNodeFactoryFqName = FqName("io.element.android.libraries.architecture.AssistedNodeFactory") private val nodeKeyFqName = FqName("io.element.android.libraries.architecture.NodeKey") } diff --git a/enterprise b/enterprise index 95789d4011..ffc02b8d0f 160000 --- a/enterprise +++ b/enterprise @@ -1 +1 @@ -Subproject commit 95789d40119499eba8a79284df9dd2306405b099 +Subproject commit ffc02b8d0f35188c3ef8a876dc1532bfe3e533da diff --git a/fastlane/metadata/android/en-US/changelogs/202510000.txt b/fastlane/metadata/android/en-US/changelogs/202510000.txt new file mode 100644 index 0000000000..e30ec573cc --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/202510000.txt @@ -0,0 +1,2 @@ +Main changes in this version: Spaces! +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInNode.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInNode.kt index 901f3047d1..b952bdb4e1 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInNode.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInNode.kt @@ -16,14 +16,14 @@ 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.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.appconfig.AnalyticsConfig import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab @ContributesNode(AppScope::class) -@Inject +@AssistedInject class AnalyticsOptInNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/announcement/api/build.gradle.kts b/features/announcement/api/build.gradle.kts new file mode 100644 index 0000000000..0fa87f039e --- /dev/null +++ b/features/announcement/api/build.gradle.kts @@ -0,0 +1,13 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ +plugins { + id("io.element.android-compose-library") +} + +android { + namespace = "io.element.android.features.announcement.api" +} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceEvents.kt b/features/announcement/api/src/main/kotlin/io/element/android/features/announcement/api/Announcement.kt similarity index 62% rename from features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceEvents.kt rename to features/announcement/api/src/main/kotlin/io/element/android/features/announcement/api/Announcement.kt index 848dac3ebc..21b83e7449 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceEvents.kt +++ b/features/announcement/api/src/main/kotlin/io/element/android/features/announcement/api/Announcement.kt @@ -5,8 +5,9 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.space.impl +package io.element.android.features.announcement.api -sealed interface SpaceEvents { - data object LoadMore : SpaceEvents +enum class Announcement { + Space, + NewNotificationSound, } diff --git a/features/announcement/api/src/main/kotlin/io/element/android/features/announcement/api/AnnouncementService.kt b/features/announcement/api/src/main/kotlin/io/element/android/features/announcement/api/AnnouncementService.kt new file mode 100644 index 0000000000..a98c0af199 --- /dev/null +++ b/features/announcement/api/src/main/kotlin/io/element/android/features/announcement/api/AnnouncementService.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2025 New Vector 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.announcement.api + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import kotlinx.coroutines.flow.Flow + +interface AnnouncementService { + suspend fun showAnnouncement(announcement: Announcement) + + suspend fun onAnnouncementDismissed(announcement: Announcement) + + fun announcementsToShowFlow(): Flow> + + /** + * Use this composable to render the announcement UI in Fullscreen. + */ + @Composable + fun Render( + modifier: Modifier, + ) +} diff --git a/features/announcement/impl/build.gradle.kts b/features/announcement/impl/build.gradle.kts new file mode 100644 index 0000000000..222d080d6e --- /dev/null +++ b/features/announcement/impl/build.gradle.kts @@ -0,0 +1,37 @@ +import extension.setupDependencyInjection +import extension.testCommonDependencies + +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +plugins { + id("io.element.android-compose-library") +} + +android { + namespace = "io.element.android.features.announcement.impl" + + testOptions { + unitTests { + isIncludeAndroidResources = true + } + } +} + +setupDependencyInjection() + +dependencies { + implementation(projects.libraries.architecture) + implementation(projects.libraries.designsystem) + implementation(projects.libraries.preferences.api) + implementation(projects.libraries.uiStrings) + api(projects.features.announcement.api) + implementation(libs.androidx.datastore.preferences) + + testCommonDependencies(libs, true) + testImplementation(projects.libraries.matrix.test) +} diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/AnnouncementPresenter.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/AnnouncementPresenter.kt new file mode 100644 index 0000000000..432aad4a0a --- /dev/null +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/AnnouncementPresenter.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import dev.zacsweers.metro.Inject +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.announcement.impl.store.AnnouncementStatus +import io.element.android.features.announcement.impl.store.AnnouncementStore +import io.element.android.libraries.architecture.Presenter +import kotlinx.coroutines.flow.map + +@Inject +class AnnouncementPresenter( + private val announcementStore: AnnouncementStore, +) : Presenter { + @Composable + override fun present(): AnnouncementState { + val showSpaceAnnouncement by remember { + announcementStore.announcementStatusFlow(Announcement.Space).map { + it == AnnouncementStatus.Show + } + }.collectAsState(false) + return AnnouncementState( + showSpaceAnnouncement = showSpaceAnnouncement, + ) + } +} diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/AnnouncementState.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/AnnouncementState.kt new file mode 100644 index 0000000000..c8ea728d64 --- /dev/null +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/AnnouncementState.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl + +data class AnnouncementState( + val showSpaceAnnouncement: Boolean, +) + +fun anAnnouncementState( + showSpaceAnnouncement: Boolean = false, +) = AnnouncementState( + showSpaceAnnouncement = showSpaceAnnouncement, +) diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementService.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementService.kt new file mode 100644 index 0000000000..6d616f14e9 --- /dev/null +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementService.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.announcement.api.AnnouncementService +import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementState +import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementView +import io.element.android.features.announcement.impl.store.AnnouncementStatus +import io.element.android.features.announcement.impl.store.AnnouncementStore +import io.element.android.libraries.architecture.Presenter +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.first + +@ContributesBinding(AppScope::class) +@Inject +class DefaultAnnouncementService( + private val announcementStore: AnnouncementStore, + private val announcementPresenter: Presenter, + private val spaceAnnouncementPresenter: Presenter, +) : AnnouncementService { + override suspend fun showAnnouncement(announcement: Announcement) { + when (announcement) { + Announcement.Space -> showSpaceAnnouncement() + Announcement.NewNotificationSound -> { + announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStatus.Show) + } + } + } + + override suspend fun onAnnouncementDismissed(announcement: Announcement) { + announcementStore.setAnnouncementStatus(announcement, AnnouncementStatus.Shown) + } + + override fun announcementsToShowFlow(): Flow> { + return combine( + announcementStore.announcementStatusFlow(Announcement.Space), + announcementStore.announcementStatusFlow(Announcement.NewNotificationSound), + ) { spaceAnnouncementStatus, newNotificationSoundStatus -> + buildList { + if (spaceAnnouncementStatus == AnnouncementStatus.Show) { + add(Announcement.Space) + } + if (newNotificationSoundStatus == AnnouncementStatus.Show) { + add(Announcement.NewNotificationSound) + } + } + } + } + + private suspend fun showSpaceAnnouncement() { + val currentValue = announcementStore.announcementStatusFlow(Announcement.Space).first() + if (currentValue == AnnouncementStatus.NeverShown) { + announcementStore.setAnnouncementStatus(Announcement.Space, AnnouncementStatus.Show) + } + } + + @Composable + override fun Render(modifier: Modifier) { + val announcementState = announcementPresenter.present() + Box(modifier = modifier.fillMaxSize()) { + AnimatedVisibility( + visible = announcementState.showSpaceAnnouncement, + enter = fadeIn(), + exit = fadeOut(), + ) { + val spaceAnnouncementState = spaceAnnouncementPresenter.present() + SpaceAnnouncementView( + state = spaceAnnouncementState, + ) + } + } + } +} diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/di/AnnouncementModule.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/di/AnnouncementModule.kt new file mode 100644 index 0000000000..4fbc9118bc --- /dev/null +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/di/AnnouncementModule.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl.di + +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.BindingContainer +import dev.zacsweers.metro.Binds +import dev.zacsweers.metro.ContributesTo +import io.element.android.features.announcement.impl.AnnouncementPresenter +import io.element.android.features.announcement.impl.AnnouncementState +import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementPresenter +import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementState +import io.element.android.libraries.architecture.Presenter + +@ContributesTo(AppScope::class) +@BindingContainer +interface AnnouncementModule { + @Binds + fun bindAnnouncementPresenter(presenter: AnnouncementPresenter): Presenter + + @Binds + fun bindSpaceAnnouncementPresenter(presenter: SpaceAnnouncementPresenter): Presenter +} diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementEvents.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementEvents.kt new file mode 100644 index 0000000000..9741608b1e --- /dev/null +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementEvents.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl.spaces + +sealed interface SpaceAnnouncementEvents { + data object Continue : SpaceAnnouncementEvents +} diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementPresenter.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementPresenter.kt new file mode 100644 index 0000000000..4d1b2f3e4b --- /dev/null +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementPresenter.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl.spaces + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import dev.zacsweers.metro.Inject +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.announcement.impl.store.AnnouncementStatus +import io.element.android.features.announcement.impl.store.AnnouncementStore +import io.element.android.libraries.architecture.Presenter +import kotlinx.coroutines.launch + +@Inject +class SpaceAnnouncementPresenter( + private val announcementStore: AnnouncementStore, +) : Presenter { + @Composable + override fun present(): SpaceAnnouncementState { + val localCoroutineScope = rememberCoroutineScope() + + fun handleEvents(event: SpaceAnnouncementEvents) { + when (event) { + SpaceAnnouncementEvents.Continue -> localCoroutineScope.launch { + announcementStore.setAnnouncementStatus(Announcement.Space, AnnouncementStatus.Shown) + } + } + } + + return SpaceAnnouncementState( + eventSink = ::handleEvents + ) + } +} diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementState.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementState.kt new file mode 100644 index 0000000000..7628ed27ae --- /dev/null +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementState.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl.spaces + +data class SpaceAnnouncementState( + val eventSink: (SpaceAnnouncementEvents) -> Unit +) diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementStateProvider.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementStateProvider.kt new file mode 100644 index 0000000000..d994edf3d8 --- /dev/null +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementStateProvider.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl.spaces + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider + +open class SpaceAnnouncementStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aSpaceAnnouncementState(), + ) +} + +fun aSpaceAnnouncementState( + eventSink: (SpaceAnnouncementEvents) -> Unit = {}, +) = SpaceAnnouncementState( + eventSink = eventSink, +) diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementView.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementView.kt new file mode 100644 index 0000000000..2a8c5257aa --- /dev/null +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementView.kt @@ -0,0 +1,157 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl.spaces + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +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.announcement.impl.R +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.organisms.InfoListItem +import io.element.android.libraries.designsystem.atomic.organisms.InfoListOrganism +import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage +import io.element.android.libraries.designsystem.components.BigIcon +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Button +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.persistentListOf + +/** + * Ref: https://www.figma.com/design/kcnHxunG1LDWXsJhaNuiHz/ER-145--Spaces-on-Element-X?node-id=4593-40181 + */ +@Composable +fun SpaceAnnouncementView( + state: SpaceAnnouncementState, + modifier: Modifier = Modifier, +) { + val eventSink = state.eventSink + + fun onContinue() { + eventSink(SpaceAnnouncementEvents.Continue) + } + + BackHandler(onBack = ::onContinue) + HeaderFooterPage( + modifier = modifier, + isScrollable = true, + contentPadding = PaddingValues(top = 24.dp, start = 16.dp, end = 16.dp, bottom = 24.dp), + header = { + SpaceAnnouncementHeader() + }, + content = { + SpaceAnnouncementContent( + modifier = Modifier.padding(horizontal = 8.dp), + ) + }, + footer = { + SpaceAnnouncementFooter( + onContinue = ::onContinue, + ) + } + ) +} + +@Composable +private fun SpaceAnnouncementHeader( + modifier: Modifier = Modifier, +) { + IconTitleSubtitleMolecule( + modifier = modifier.padding(top = 16.dp, bottom = 16.dp), + title = stringResource(id = R.string.screen_space_announcement_title), + showBetaLabel = true, + subTitle = stringResource(id = R.string.screen_space_announcement_subtitle), + iconStyle = BigIcon.Style.Default( + vectorIcon = CompoundIcons.WorkspaceSolid(), + usePrimaryTint = true, + ), + ) +} + +@Composable +private fun SpaceAnnouncementContent( + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier.fillMaxSize(), + ) { + InfoListOrganism( + modifier = Modifier.fillMaxWidth(), + items = persistentListOf( + InfoListItem( + message = stringResource(id = R.string.screen_space_announcement_item1), + iconVector = CompoundIcons.VisibilityOn(), + ), + InfoListItem( + message = stringResource(id = R.string.screen_space_announcement_item2), + iconVector = CompoundIcons.Email(), + ), + InfoListItem( + message = stringResource(id = R.string.screen_space_announcement_item3), + iconVector = CompoundIcons.Search(), + ), + InfoListItem( + message = stringResource(id = R.string.screen_space_announcement_item4), + iconVector = CompoundIcons.Explore(), + ), + InfoListItem( + message = stringResource(id = R.string.screen_space_announcement_item5), + iconVector = CompoundIcons.Leave(), + ), + ), + textStyle = ElementTheme.typography.fontBodyLgMedium, + iconTint = ElementTheme.colors.iconSecondary, + iconSize = 24.dp + ) + Text( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp), + text = stringResource(id = R.string.screen_space_announcement_notice), + style = ElementTheme.typography.fontBodyMdRegular, + color = ElementTheme.colors.textSecondary, + textAlign = TextAlign.Center, + ) + } +} + +@Composable +private fun SpaceAnnouncementFooter( + onContinue: () -> Unit, +) { + ButtonColumnMolecule( + modifier = Modifier.padding(bottom = 8.dp) + ) { + Button( + text = stringResource(id = CommonStrings.action_continue), + onClick = onContinue, + modifier = Modifier.fillMaxWidth(), + ) + } +} + +@PreviewsDayNight +@Composable +internal fun SpaceAnnouncementViewPreview(@PreviewParameter(SpaceAnnouncementStateProvider::class) state: SpaceAnnouncementState) = ElementPreview { + SpaceAnnouncementView( + state = state, + ) +} diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/AnnouncementStatus.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/AnnouncementStatus.kt new file mode 100644 index 0000000000..e275a140e1 --- /dev/null +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/AnnouncementStatus.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl.store + +enum class AnnouncementStatus { + NeverShown, + Show, + Shown, +} diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/AnnouncementStore.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/AnnouncementStore.kt new file mode 100644 index 0000000000..3385165c52 --- /dev/null +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/AnnouncementStore.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl.store + +import io.element.android.features.announcement.api.Announcement +import kotlinx.coroutines.flow.Flow + +interface AnnouncementStore { + suspend fun setAnnouncementStatus( + announcement: Announcement, + status: AnnouncementStatus, + ) + + fun announcementStatusFlow( + announcement: Announcement, + ): Flow + + suspend fun reset() +} diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt new file mode 100644 index 0000000000..37acf9f6b4 --- /dev/null +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl.store + +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.intPreferencesKey +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import io.element.android.features.announcement.api.Announcement +import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +private val spaceAnnouncementKey = intPreferencesKey("spaceAnnouncement") +private val newNotificationSoundKey = intPreferencesKey("newNotificationSound") + +@ContributesBinding(AppScope::class) +@Inject +class DefaultAnnouncementStore( + preferenceDataStoreFactory: PreferenceDataStoreFactory, +) : AnnouncementStore { + private val store = preferenceDataStoreFactory.create("elementx_announcement") + + override suspend fun setAnnouncementStatus(announcement: Announcement, status: AnnouncementStatus) { + val key = announcement.toKey() + store.edit { prefs -> + prefs[key] = status.ordinal + } + } + + override fun announcementStatusFlow(announcement: Announcement): Flow { + val key = announcement.toKey() + // For NewNotificationSound, a migration will set it to Show on application upgrade (see AppMigration08) + val defaultStatus = when (announcement) { + Announcement.Space -> AnnouncementStatus.NeverShown + Announcement.NewNotificationSound -> AnnouncementStatus.Shown + } + return store.data.map { prefs -> + val ordinal = prefs[key] ?: defaultStatus.ordinal + AnnouncementStatus.entries.getOrElse(ordinal) { defaultStatus } + } + } + + override suspend fun reset() { + store.edit { it.clear() } + } +} + +private fun Announcement.toKey() = when (this) { + Announcement.Space -> spaceAnnouncementKey + Announcement.NewNotificationSound -> newNotificationSoundKey +} diff --git a/features/announcement/impl/src/main/res/values-da/translations.xml b/features/announcement/impl/src/main/res/values-da/translations.xml new file mode 100644 index 0000000000..933ae95d6d --- /dev/null +++ b/features/announcement/impl/src/main/res/values-da/translations.xml @@ -0,0 +1,11 @@ + + + "Se klynger, du har oprettet eller tilmeldt dig" + "Acceptere eller afvise invitationer til klynger" + "Finde alle rum, du kan deltage i, i dine klynger" + "Deltage i offentlige klynger" + "Forlade de klynger, du har tilsluttet dig" + "Oprettelse og administration af klynger kommer snart." + "Velkommen til betaversionen af Klynger! Med denne første version kan du:" + "Introduktion til Klynger" + diff --git a/features/announcement/impl/src/main/res/values-de/translations.xml b/features/announcement/impl/src/main/res/values-de/translations.xml new file mode 100644 index 0000000000..c38a9b5cd9 --- /dev/null +++ b/features/announcement/impl/src/main/res/values-de/translations.xml @@ -0,0 +1,11 @@ + + + "Von dir erstellte oder beigetretene Spaces anzeigen" + "Einladungen zu Spaces annehmen oder ablehnen" + "Chats innerhalb deiner Spaces entdecken, um ihnen beizutreten" + "Öffentlichen Spaces beitreten" + "Spaces verlassen, bei denen du Mitglied bist" + "Das Erstellen und Verwalten von Spaces ist bald verfügbar." + "Willkommen bei der Beta-Version von Spaces! Mit dieser ersten Version kannst du:" + "Einführung in Spaces" + diff --git a/features/announcement/impl/src/main/res/values-fi/translations.xml b/features/announcement/impl/src/main/res/values-fi/translations.xml new file mode 100644 index 0000000000..a5de5465ec --- /dev/null +++ b/features/announcement/impl/src/main/res/values-fi/translations.xml @@ -0,0 +1,11 @@ + + + "Nähdä luomasi tai liittymäsi tilat" + "Hyväksyä tai hylätä kutsuja tiloihin" + "Löytää kaikki huoneet, joihin voit liittyä tiloissasi" + "Liittyä julkisiin tiloihin" + "Poistua mistä tahansa tilasta, johon olet liittynyt" + "Tilojen luominen ja hallinta on tulossa pian." + "Tervetuloa tilojen beetaversioon! Tämän ensimmäisen version avulla voit:" + "Esittelyssä tilat" + diff --git a/features/announcement/impl/src/main/res/values-fr/translations.xml b/features/announcement/impl/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..04a35b0fc0 --- /dev/null +++ b/features/announcement/impl/src/main/res/values-fr/translations.xml @@ -0,0 +1,11 @@ + + + "Voir les espaces que vous avez créés ou rejoints" + "Accepter ou refuser les invitations aux espaces" + "Découvrir les salons que vous pouvez joindre depuis vos espaces" + "Rejoindre les espaces publics" + "Quitter les espaces dont vous êtes membre." + "La création et la gestion des espaces seront bientôt disponibles." + "Bienvenue dans la version bêta des espaces! Avec cette première version, vous pourrez :" + "Ajout des espaces" + diff --git a/features/announcement/impl/src/main/res/values-nb/translations.xml b/features/announcement/impl/src/main/res/values-nb/translations.xml new file mode 100644 index 0000000000..0765e557f8 --- /dev/null +++ b/features/announcement/impl/src/main/res/values-nb/translations.xml @@ -0,0 +1,11 @@ + + + "Se områder du har opprettet eller blitt med i" + "Godta eller avslå invitasjoner til områder" + "Oppdag alle rom du kan bli med i i dine områder" + "Bli med i offentlige områder" + "Forlat områder du har blitt med i" + "Oppretting og administrasjon av områder kommer snart." + "Velkommen til betaversjonen av Områder! Med denne første versjonen kan du:" + "Vi introduserer Områder" + diff --git a/features/announcement/impl/src/main/res/values-ro/translations.xml b/features/announcement/impl/src/main/res/values-ro/translations.xml new file mode 100644 index 0000000000..48fa06fca4 --- /dev/null +++ b/features/announcement/impl/src/main/res/values-ro/translations.xml @@ -0,0 +1,11 @@ + + + "Vizualizați spațiile pe care le-ați creat sau la care v-ați alăturat" + "Acceptați sau refuzați invitațiile la spații" + "Descoperiți toate camerele la care vă puteți alătura în spațiile dumneavoastră." + "Alăturați-vă spațiilor publice" + "Părăsiți spațiile la care v-ați alăturat." + "Crearea și gestionarea spațiilor vor fi disponibile în curând." + "Bun venit la versiunea beta a Spațiilor! Cu această primă versiune puteți:" + "Vă prezentăm Spații" + diff --git a/features/announcement/impl/src/main/res/values-ru/translations.xml b/features/announcement/impl/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..8b930b2ca0 --- /dev/null +++ b/features/announcement/impl/src/main/res/values-ru/translations.xml @@ -0,0 +1,11 @@ + + + "Просмотр пространств, которые вы создали или к которым присоединились" + "Принимать или отклонять приглашения в пространства" + "Откройте для себя все комнаты, к которым вы можете присоединиться в своих пространствах." + "Присоединиться к публичному пространству" + "Покинуть все пространства, к которым вы присоединились" + "Создание и управление пространствами станет доступно в ближайшее время." + "Добро пожаловать в бета-версию Spaces! В этой первой версии вы сможете:" + "Знакомство с пространствами" + diff --git a/features/announcement/impl/src/main/res/values-zh/translations.xml b/features/announcement/impl/src/main/res/values-zh/translations.xml new file mode 100644 index 0000000000..66608eb6e5 --- /dev/null +++ b/features/announcement/impl/src/main/res/values-zh/translations.xml @@ -0,0 +1,5 @@ + + + "查看您创建或加入的空间" + "接受或拒绝空间邀请" + diff --git a/features/announcement/impl/src/main/res/values/localazy.xml b/features/announcement/impl/src/main/res/values/localazy.xml new file mode 100644 index 0000000000..5e7b8a6713 --- /dev/null +++ b/features/announcement/impl/src/main/res/values/localazy.xml @@ -0,0 +1,11 @@ + + + "View spaces you\'ve created or joined" + "Accept or decline invites to spaces" + "Discover any rooms you can join in your spaces" + "Join public spaces" + "Leave any spaces you’ve joined" + "Filtering, creating and managing spaces is coming soon." + "Welcome to the beta version of Spaces! With this first version you can:" + "Introducing Spaces" + diff --git a/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/AnnouncementPresenterTest.kt b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/AnnouncementPresenterTest.kt new file mode 100644 index 0000000000..9290773b30 --- /dev/null +++ b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/AnnouncementPresenterTest.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.announcement.impl.store.AnnouncementStatus +import io.element.android.features.announcement.impl.store.AnnouncementStore +import io.element.android.features.announcement.impl.store.InMemoryAnnouncementStore +import io.element.android.tests.testutils.test +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class AnnouncementPresenterTest { + @Test + fun `present - initial state`() = runTest { + val presenter = createAnnouncementPresenter() + presenter.test { + val state = awaitItem() + assertThat(state.showSpaceAnnouncement).isFalse() + } + } + + @Test + fun `present - showSpaceAnnouncement value depends on the value in the store`() = runTest { + val store = InMemoryAnnouncementStore() + val presenter = createAnnouncementPresenter( + announcementStore = store, + ) + presenter.test { + val state = awaitItem() + assertThat(state.showSpaceAnnouncement).isFalse() + store.setAnnouncementStatus(Announcement.Space, AnnouncementStatus.Show) + val updatedState = awaitItem() + assertThat(updatedState.showSpaceAnnouncement).isTrue() + store.setAnnouncementStatus(Announcement.Space, AnnouncementStatus.Shown) + val finalState = awaitItem() + assertThat(finalState.showSpaceAnnouncement).isFalse() + } + } +} + +private fun createAnnouncementPresenter( + announcementStore: AnnouncementStore = InMemoryAnnouncementStore(), +) = AnnouncementPresenter( + announcementStore = announcementStore, +) diff --git a/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementServiceTest.kt b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementServiceTest.kt new file mode 100644 index 0000000000..990f506223 --- /dev/null +++ b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementServiceTest.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl + +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.announcement.impl.spaces.SpaceAnnouncementState +import io.element.android.features.announcement.impl.spaces.aSpaceAnnouncementState +import io.element.android.features.announcement.impl.store.AnnouncementStatus +import io.element.android.features.announcement.impl.store.AnnouncementStore +import io.element.android.features.announcement.impl.store.InMemoryAnnouncementStore +import io.element.android.libraries.architecture.Presenter +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class DefaultAnnouncementServiceTest { + @Test + fun `when showing Space announcement, space announcement is set to show only if it was never shown`() = runTest { + val announcementStore = InMemoryAnnouncementStore() + val sut = createDefaultAnnouncementService( + announcementStore = announcementStore, + ) + assertThat(announcementStore.announcementStatusFlow(Announcement.Space).first()).isEqualTo(AnnouncementStatus.NeverShown) + sut.showAnnouncement(Announcement.Space) + assertThat(announcementStore.announcementStatusFlow(Announcement.Space).first()).isEqualTo(AnnouncementStatus.Show) + // Simulate user close the announcement + sut.onAnnouncementDismissed(Announcement.Space) + // Entering again the space tab should not change the value + sut.showAnnouncement(Announcement.Space) + assertThat(announcementStore.announcementStatusFlow(Announcement.Space).first()).isEqualTo(AnnouncementStatus.Shown) + } + + @Test + fun `when showing NewNotificationSound announcement, announcement is set to show even if it was already shown`() = runTest { + val announcementStore = InMemoryAnnouncementStore() + val sut = createDefaultAnnouncementService( + announcementStore = announcementStore, + ) + assertThat(announcementStore.announcementStatusFlow(Announcement.NewNotificationSound).first()).isEqualTo(AnnouncementStatus.NeverShown) + sut.showAnnouncement(Announcement.NewNotificationSound) + assertThat(announcementStore.announcementStatusFlow(Announcement.NewNotificationSound).first()).isEqualTo(AnnouncementStatus.Show) + // Simulate user close the announcement + sut.onAnnouncementDismissed(Announcement.NewNotificationSound) + // Calling again showAnnouncement should set it back to Show + sut.showAnnouncement(Announcement.NewNotificationSound) + assertThat(announcementStore.announcementStatusFlow(Announcement.NewNotificationSound).first()).isEqualTo(AnnouncementStatus.Show) + } + + @Test + fun `test announcementsToShowFlow`() = runTest { + val announcementStore = InMemoryAnnouncementStore() + val sut = createDefaultAnnouncementService( + announcementStore = announcementStore, + ) + sut.announcementsToShowFlow().test { + assertThat(awaitItem()).isEmpty() + announcementStore.setAnnouncementStatus(Announcement.Space, AnnouncementStatus.Show) + assertThat(awaitItem()).containsExactly(Announcement.Space) + announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStatus.Show) + assertThat(awaitItem()).containsExactly(Announcement.Space, Announcement.NewNotificationSound) + announcementStore.setAnnouncementStatus(Announcement.Space, AnnouncementStatus.Shown) + assertThat(awaitItem()).containsExactly(Announcement.NewNotificationSound) + announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStatus.Shown) + assertThat(awaitItem()).isEmpty() + } + } + + private fun createDefaultAnnouncementService( + announcementStore: AnnouncementStore = InMemoryAnnouncementStore(), + announcementPresenter: Presenter = Presenter { anAnnouncementState() }, + spaceAnnouncementPresenter: Presenter = Presenter { aSpaceAnnouncementState() }, + ) = DefaultAnnouncementService( + announcementStore = announcementStore, + announcementPresenter = announcementPresenter, + spaceAnnouncementPresenter = spaceAnnouncementPresenter, + ) +} diff --git a/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementPresenterTest.kt b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementPresenterTest.kt new file mode 100644 index 0000000000..2c35bccf41 --- /dev/null +++ b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementPresenterTest.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl.spaces + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.announcement.impl.store.AnnouncementStatus +import io.element.android.features.announcement.impl.store.AnnouncementStore +import io.element.android.features.announcement.impl.store.InMemoryAnnouncementStore +import io.element.android.tests.testutils.test +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class SpaceAnnouncementPresenterTest { + @Test + fun `present - when user continues, the store is updated`() = runTest { + val store = InMemoryAnnouncementStore() + val presenter = createSpaceAnnouncementPresenter( + announcementStore = store, + ) + presenter.test { + assertThat(store.announcementStatusFlow(Announcement.Space).first()).isEqualTo(AnnouncementStatus.NeverShown) + val state = awaitItem() + state.eventSink(SpaceAnnouncementEvents.Continue) + assertThat(store.announcementStatusFlow(Announcement.Space).first()).isEqualTo(AnnouncementStatus.Shown) + } + } +} + +private fun createSpaceAnnouncementPresenter( + announcementStore: AnnouncementStore = InMemoryAnnouncementStore(), +) = SpaceAnnouncementPresenter( + announcementStore = announcementStore, +) diff --git a/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementViewTest.kt b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementViewTest.kt new file mode 100644 index 0000000000..96b98668a4 --- /dev/null +++ b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementViewTest.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl.spaces + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.clickOn +import io.element.android.tests.testutils.pressBackKey +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestRule +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SpaceAnnouncementViewTest { + @get:Rule val rule = createAndroidComposeRule() + + @Test + fun `clicking on back sends a SpaceAnnouncementEvents`() { + val eventsRecorder = EventsRecorder() + rule.setSpaceAnnouncementView( + aSpaceAnnouncementState( + eventSink = eventsRecorder, + ), + ) + rule.pressBackKey() + eventsRecorder.assertSingle(SpaceAnnouncementEvents.Continue) + } + + @Test + fun `clicking on Continue sends a SpaceAnnouncementEvents`() { + val eventsRecorder = EventsRecorder() + rule.setSpaceAnnouncementView( + aSpaceAnnouncementState( + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.action_continue) + eventsRecorder.assertSingle(SpaceAnnouncementEvents.Continue) + } +} + +private fun AndroidComposeTestRule.setSpaceAnnouncementView( + state: SpaceAnnouncementState, +) { + setContent { + SpaceAnnouncementView( + state = state, + ) + } +} diff --git a/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/store/InMemoryAnnouncementStore.kt b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/store/InMemoryAnnouncementStore.kt new file mode 100644 index 0000000000..f7d438784a --- /dev/null +++ b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/store/InMemoryAnnouncementStore.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2025 New Vector 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.announcement.impl.store + +import io.element.android.features.announcement.api.Announcement +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow + +class InMemoryAnnouncementStore( + initialSpaceAnnouncementStatus: AnnouncementStatus = AnnouncementStatus.NeverShown, + initialNewNotificationSoundAnnouncementStatus: AnnouncementStatus = AnnouncementStatus.NeverShown, +) : AnnouncementStore { + private val spaceAnnouncement = MutableStateFlow(initialSpaceAnnouncementStatus) + private val newNotificationSoundAnnouncement = MutableStateFlow(initialNewNotificationSoundAnnouncementStatus) + + override suspend fun setAnnouncementStatus(announcement: Announcement, status: AnnouncementStatus) { + announcement.toMutableStateFlow().value = status + } + + override fun announcementStatusFlow(announcement: Announcement): Flow { + return announcement.toMutableStateFlow().asStateFlow() + } + + override suspend fun reset() { + spaceAnnouncement.value = AnnouncementStatus.NeverShown + newNotificationSoundAnnouncement.value = AnnouncementStatus.NeverShown + } + + private fun Announcement.toMutableStateFlow() = when (this) { + Announcement.Space -> spaceAnnouncement + Announcement.NewNotificationSound -> newNotificationSoundAnnouncement + } +} diff --git a/features/announcement/test/build.gradle.kts b/features/announcement/test/build.gradle.kts new file mode 100644 index 0000000000..9387dc0caf --- /dev/null +++ b/features/announcement/test/build.gradle.kts @@ -0,0 +1,19 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ +plugins { + id("io.element.android-compose-library") +} + +android { + namespace = "io.element.android.features.announcement.test" +} + +dependencies { + implementation(projects.features.announcement.api) + implementation(libs.coroutines.core) + implementation(projects.tests.testutils) +} diff --git a/features/announcement/test/src/main/kotlin/io/element/android/features/rageshake/test/logs/FakeAnnouncementService.kt b/features/announcement/test/src/main/kotlin/io/element/android/features/rageshake/test/logs/FakeAnnouncementService.kt new file mode 100644 index 0000000000..74c2adf95f --- /dev/null +++ b/features/announcement/test/src/main/kotlin/io/element/android/features/rageshake/test/logs/FakeAnnouncementService.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2024 New Vector 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.rageshake.test.logs + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.announcement.api.AnnouncementService +import io.element.android.tests.testutils.lambda.lambdaError +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow + +class FakeAnnouncementService( + initialAnnouncementsToShowFlowValue: List = emptyList(), + val showAnnouncementResult: (Announcement) -> Unit = { lambdaError() }, + val onAnnouncementDismissedResult: (Announcement) -> Unit = { lambdaError() }, + val renderResult: (Modifier) -> Unit = { lambdaError() }, +) : AnnouncementService { + private val announcementsToShowFlowValue = MutableStateFlow(initialAnnouncementsToShowFlowValue) + + override suspend fun showAnnouncement(announcement: Announcement) { + showAnnouncementResult(announcement) + } + + override suspend fun onAnnouncementDismissed(announcement: Announcement) { + onAnnouncementDismissedResult(announcement) + } + + override fun announcementsToShowFlow(): Flow> { + return announcementsToShowFlowValue.asStateFlow() + } + + fun emitAnnouncementsToShow(value: List) { + announcementsToShowFlowValue.value = value + } + + @Composable + override fun Render(modifier: Modifier) { + renderResult(modifier) + } +} diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt index c1c58d4607..06929093f5 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt @@ -19,7 +19,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.compound.theme.ElementTheme import io.element.android.features.call.api.CallType @@ -49,7 +49,7 @@ import timber.log.Timber import java.util.UUID import kotlin.time.Duration.Companion.seconds -@Inject +@AssistedInject class CallScreenPresenter( @Assisted private val callType: CallType, @Assisted private val navigator: CallScreenNavigator, @@ -242,7 +242,7 @@ class CallScreenPresenter( } coroutineScope.launch { Timber.d("Observing sync state in-call for sessionId: ${roomCallType.sessionId}") - client.syncService().syncState + client.syncService.syncState .collect { state -> if (state != SyncState.Running) { appForegroundStateService.updateIsInCallState(true) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewWidgetMessageInterceptor.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewWidgetMessageInterceptor.kt index 55adc246e9..82500c5937 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewWidgetMessageInterceptor.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewWidgetMessageInterceptor.kt @@ -133,6 +133,7 @@ class WebViewWidgetMessageInterceptor( return assetLoader.shouldInterceptRequest(request.url) } + @Suppress("OVERRIDE_DEPRECATION") override fun shouldInterceptRequest(view: WebView?, url: String): WebResourceResponse? { return assetLoader.shouldInterceptRequest(url.toUri()) } diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNode.kt b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNode.kt index 697297372f..1b9c790b25 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNode.kt +++ b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNode.kt @@ -15,7 +15,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType import io.element.android.libraries.architecture.NodeInputs @@ -26,7 +26,7 @@ import io.element.android.libraries.matrix.api.room.RoomMember import kotlinx.coroutines.flow.first @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class ChangeRolesNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesPresenter.kt b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesPresenter.kt index cf43874b10..632d93f0e0 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesPresenter.kt +++ b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesPresenter.kt @@ -20,7 +20,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.RoomModeration import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter @@ -37,17 +37,15 @@ import io.element.android.libraries.matrix.ui.model.roleOf import io.element.android.libraries.matrix.ui.room.PowerLevelRoomMemberComparator import io.element.android.services.analytics.api.AnalyticsService import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -@Inject +@AssistedInject class ChangeRolesPresenter( @Assisted private val role: RoomMember.Role, private val room: JoinedRoom, @@ -73,11 +71,11 @@ class ChangeRolesPresenter( } val exitState: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val saveState: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } - val usersWithRole = produceState(initialValue = persistentListOf()) { + val usersWithRole = produceState>(initialValue = persistentListOf()) { room.usersWithRole(role).map { members -> members.map { it.toMatrixUser() } } .onEach { users -> - val previous: PersistentList = value - value = users.toPersistentList() + val previous = value + value = users.toImmutableList() // Users who were selected but didn't have the role, so their role change was pending val toAdd = selectedUsers.value.filter { user -> users.none { it.userId == user.userId } && previous.none { it.userId == user.userId } } // Users who no longer have the role diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRoomMemberRolesRootNode.kt b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRoomMemberRolesRootNode.kt index 5055761e6e..2c3f77f208 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRoomMemberRolesRootNode.kt +++ b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRoomMemberRolesRootNode.kt @@ -17,9 +17,9 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.ParentNode import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode -import io.element.android.appnav.di.RoomComponentFactory +import io.element.android.appnav.di.RoomGraphFactory import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType import io.element.android.libraries.architecture.NodeInputs @@ -32,11 +32,11 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class ChangeRoomMemberRolesRootNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, - roomComponentFactory: RoomComponentFactory, + roomGraphFactory: RoomGraphFactory, ) : ParentNode( navModel = PermanentNavModel( navTargets = setOf(NavTarget), @@ -54,7 +54,7 @@ class ChangeRoomMemberRolesRootNode( private val inputs = inputs() - override val graph = roomComponentFactory.create(inputs.joinedRoom) + override val graph = roomGraphFactory.create(inputs.joinedRoom) override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return createNode( diff --git a/features/changeroommemberroles/impl/src/main/res/values-nb/translations.xml b/features/changeroommemberroles/impl/src/main/res/values-nb/translations.xml index 33c9fabe24..2e3379b98a 100644 --- a/features/changeroommemberroles/impl/src/main/res/values-nb/translations.xml +++ b/features/changeroommemberroles/impl/src/main/res/values-nb/translations.xml @@ -17,6 +17,7 @@ "Rediger administratorer" "Du vil ikke kunne angre denne handlingen. Du forfremmer brukeren til å ha samme rettighetsnivå som deg." "Legg til administrator?" + "Du kan ikke angre denne handlingen. Du overfører eierskapet til de valgte brukerne. Når du forlater siden, vil dette være permanent." "Overføre eierskapet?" "Degradere" "Du vil ikke kunne angre denne endringen ettersom du degraderer deg selv, og hvis du er den siste privilegerte brukeren i rommet, vil det være umulig å få tilbake privilegiene." diff --git a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesPresenterTest.kt b/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesPresenterTest.kt index e5e22b8846..41b6acd60c 100644 --- a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesPresenterTest.kt +++ b/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesPresenterTest.kt @@ -32,8 +32,8 @@ import io.element.android.libraries.previewutils.room.aRoomMemberList import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.collections.immutable.persistentMapOf -import kotlinx.collections.immutable.toPersistentList -import kotlinx.collections.immutable.toPersistentMap +import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test @@ -103,7 +103,7 @@ class ChangeRolesPresenterTest { // Owner - creator aRoomMember(userId = creatorUserId, role = RoomMember.Role.Owner(isCreator = true)) ) - givenRoomMembersState(RoomMembersState.Ready(roomMemberList.toPersistentList())) + givenRoomMembersState(RoomMembersState.Ready(roomMemberList.toImmutableList())) } val presenter = createChangeRolesPresenter(room = room) moleculeFlow(RecompositionMode.Immediate) { @@ -124,7 +124,7 @@ class ChangeRolesPresenterTest { val creatorUserId = UserId("@creator:matrix.org") val memberList = aRoomMemberList() .plus(aRoomMember(displayName = "CREATOR", role = RoomMember.Role.Owner(isCreator = true), userId = creatorUserId)) - .toPersistentList() + .toImmutableList() givenRoomInfo(aRoomInfo(roomCreators = listOf(creatorUserId))) givenRoomMembersState(RoomMembersState.Ready(memberList)) } @@ -203,7 +203,7 @@ class ChangeRolesPresenterTest { assertThat(initialResults?.moderators).hasSize(1) assertThat(initialResults?.admins).hasSize(1) - room.givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList().take(1).toPersistentList())) + room.givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList().take(1).toImmutableList())) skipItems(1) val searchResults = (awaitItem().searchResults as? SearchBarResultState.Results)?.results @@ -552,7 +552,7 @@ class ChangeRolesPresenterTest { private fun roomPowerLevelsWithRoles(vararg pairs: Pair): RoomPowerLevels { return RoomPowerLevels( values = defaultRoomPowerLevelValues(), - users = pairs.associate { (userId, role) -> userId to role.powerLevel }.toPersistentMap() + users = pairs.associate { (userId, role) -> userId to role.powerLevel }.toImmutableMap() ) } } diff --git a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPointTest.kt b/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPointTest.kt index 5f68fe5970..621af8edaf 100644 --- a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPointTest.kt +++ b/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPointTest.kt @@ -26,7 +26,7 @@ class DefaultChangeRoomMemberRolesEntyPointTest { ChangeRoomMemberRolesRootNode( buildContext = buildContext, plugins = plugins, - roomComponentFactory = { }, + roomGraphFactory = { }, ) } val room = FakeJoinedRoom() diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt index 099098f9bc..8f46103ba5 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt @@ -17,7 +17,7 @@ import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.replace import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.createroom.api.CreateRoomEntryPoint import io.element.android.features.createroom.impl.addpeople.AddPeopleNode @@ -30,7 +30,7 @@ import io.element.android.libraries.matrix.api.core.RoomId import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class CreateRoomFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt index fe9ff50a5f..9ea89912cb 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt @@ -14,7 +14,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.invitepeople.api.InvitePeoplePresenter import io.element.android.features.invitepeople.api.InvitePeopleRenderer @@ -24,7 +24,7 @@ import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class AddPeopleNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt index 0b61ce68ae..9e721d28bd 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt @@ -15,7 +15,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.SessionScope @@ -23,7 +23,7 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.services.analytics.api.AnalyticsService @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class ConfigureRoomNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationNode.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationNode.kt index d63e42a2db..3e554672a8 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationNode.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationNode.kt @@ -13,12 +13,12 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class AccountDeactivationNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/deactivation/impl/src/main/res/values-hu/translations.xml b/features/deactivation/impl/src/main/res/values-hu/translations.xml index 47651f0ff9..3d3722b8ef 100644 --- a/features/deactivation/impl/src/main/res/values-hu/translations.xml +++ b/features/deactivation/impl/src/main/res/values-hu/translations.xml @@ -8,7 +8,7 @@ "%1$s a fiókját (nem fog tudni újra bejelentkezni, és az azonosítója nem használható újra)." "Véglegesen letiltja" "Eltávolításra kerül az összes csevegőszobából." - "Törlésre kerülnek a fiókadatai a személyazonosító kiszolgálónkról." + "Törlésre kerülnek a fiókadatai az azonosítási kiszolgálónkról." "Üzenetei továbbra is láthatóak maradnak a regisztrált felhasználók számára, de nem lesznek elérhetőek az új vagy nem regisztrált felhasználók számára, ha úgy dönt, hogy törli őket." "Fiók deaktiválása" diff --git a/features/enterprise/api/build.gradle.kts b/features/enterprise/api/build.gradle.kts index 9f63ab2cf1..b32f42e31f 100644 --- a/features/enterprise/api/build.gradle.kts +++ b/features/enterprise/api/build.gradle.kts @@ -13,7 +13,7 @@ android { } dependencies { - implementation(libs.compound) + implementation(projects.libraries.compound) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) } diff --git a/features/enterprise/impl-foss/build.gradle.kts b/features/enterprise/impl-foss/build.gradle.kts index 956c0e1900..c5c194807f 100644 --- a/features/enterprise/impl-foss/build.gradle.kts +++ b/features/enterprise/impl-foss/build.gradle.kts @@ -18,7 +18,7 @@ android { setupDependencyInjection() dependencies { - implementation(libs.compound) + implementation(projects.libraries.compound) api(projects.features.enterprise.api) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) diff --git a/features/enterprise/test/build.gradle.kts b/features/enterprise/test/build.gradle.kts index 91b76f4fa7..38cc7aaaa9 100644 --- a/features/enterprise/test/build.gradle.kts +++ b/features/enterprise/test/build.gradle.kts @@ -14,7 +14,7 @@ android { dependencies { api(projects.features.enterprise.api) - implementation(libs.compound) + implementation(projects.libraries.compound) implementation(projects.libraries.matrix.api) implementation(projects.tests.testutils) } diff --git a/features/ftue/impl/build.gradle.kts b/features/ftue/impl/build.gradle.kts index 8f940ed29a..d7d61f6d8d 100644 --- a/features/ftue/impl/build.gradle.kts +++ b/features/ftue/impl/build.gradle.kts @@ -34,6 +34,7 @@ dependencies { implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) implementation(projects.libraries.preferences.api) + implementation(projects.libraries.uiCommon) implementation(projects.libraries.uiStrings) implementation(projects.libraries.testtags) implementation(projects.features.analytics.api) diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt index a4aa9f18d7..6552a3b360 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt @@ -8,10 +8,7 @@ package io.element.android.features.ftue.impl import android.os.Parcelable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.lifecycle.lifecycleScope import com.bumble.appyx.core.modality.BuildContext @@ -20,9 +17,8 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.newRoot import com.bumble.appyx.navmodel.backstack.operation.replace -import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.analytics.api.AnalyticsEntryPoint import io.element.android.features.ftue.impl.notifications.NotificationsOptInNode @@ -34,15 +30,15 @@ import io.element.android.features.lockscreen.api.LockScreenEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode -import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.ui.common.nodes.emptyNode import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class FtueFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, @@ -87,7 +83,7 @@ class FtueFlowNode( override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { NavTarget.Placeholder -> { - createNode(buildContext) + emptyNode(buildContext) } is NavTarget.SessionVerification -> { val callback = object : FtueSessionVerificationFlowNode.Callback { @@ -146,17 +142,3 @@ class FtueFlowNode( BackstackView() } } - -@ContributesNode(AppScope::class) -@Inject -class PlaceholderNode( - @Assisted buildContext: BuildContext, - @Assisted plugins: List, -) : Node(buildContext, plugins = plugins) { - @Composable - override fun View(modifier: Modifier) { - Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - CircularProgressIndicator() - } - } -} diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInNode.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInNode.kt index eca83cfc06..6e13e23a31 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInNode.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInNode.kt @@ -14,13 +14,13 @@ 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.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs @ContributesNode(AppScope::class) -@Inject +@AssistedInject class NotificationsOptInNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 0dbed74b02..b6f5d76351 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 @@ -14,7 +14,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.di.annotations.AppCoroutineScope import io.element.android.libraries.permissions.api.PermissionStateProvider @@ -25,7 +25,7 @@ import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -@Inject +@AssistedInject class NotificationsOptInPresenter( permissionsPresenterFactory: PermissionsPresenter.Factory, @Assisted private val callback: NotificationsOptInNode.Callback, diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/FtueSessionVerificationFlowNode.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/FtueSessionVerificationFlowNode.kt index 5141924a78..02a27d381d 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/FtueSessionVerificationFlowNode.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/FtueSessionVerificationFlowNode.kt @@ -21,7 +21,7 @@ import com.bumble.appyx.navmodel.backstack.operation.newRoot import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.appconfig.LearnMoreConfig import io.element.android.features.ftue.impl.sessionverification.choosemode.ChooseSelfVerificationModeNode @@ -37,7 +37,7 @@ import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class FtueSessionVerificationFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeNode.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeNode.kt index a03ce8c06b..99409ac2d2 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeNode.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeNode.kt @@ -14,14 +14,14 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.logout.api.direct.DirectLogoutView import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class ChooseSelfVerificationModeNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 e278e803c4..eb3c1330b3 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 @@ -26,7 +26,7 @@ class ChooseSelfVerificationModePresenter( ) : Presenter { @Composable override fun present(): ChooseSelfVerificationModeState { - val isLastDevice by encryptionService.isLastDevice.collectAsState() + val hasDevicesToVerifyAgainst by encryptionService.hasDevicesToVerifyAgainst.collectAsState() val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState() val canEnterRecoveryKey by remember { derivedStateOf { recoveryState == RecoveryState.INCOMPLETE } } @@ -39,7 +39,7 @@ class ChooseSelfVerificationModePresenter( } return ChooseSelfVerificationModeState( - isLastDevice = isLastDevice, + canUseAnotherDevice = hasDevicesToVerifyAgainst, canEnterRecoveryKey = canEnterRecoveryKey, directLogoutState = directLogoutState, eventSink = ::eventHandler, diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeState.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeState.kt index 21c37a4ae2..117768a6d2 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeState.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeState.kt @@ -10,7 +10,7 @@ package io.element.android.features.ftue.impl.sessionverification.choosemode import io.element.android.features.logout.api.direct.DirectLogoutState data class ChooseSelfVerificationModeState( - val isLastDevice: Boolean, + val canUseAnotherDevice: Boolean, val canEnterRecoveryKey: Boolean, val directLogoutState: DirectLogoutState, val eventSink: (ChooseSelfVerificationModeEvent) -> Unit, diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeStateProvider.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeStateProvider.kt index 574aa367c6..e053728e2c 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeStateProvider.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeStateProvider.kt @@ -13,18 +13,18 @@ import io.element.android.features.logout.api.direct.aDirectLogoutState class ChooseSelfVerificationModeStateProvider : PreviewParameterProvider { override val values = sequenceOf( - aChooseSelfVerificationModeState(isLastDevice = true, canEnterRecoveryKey = true), - aChooseSelfVerificationModeState(isLastDevice = true, canEnterRecoveryKey = false), - aChooseSelfVerificationModeState(isLastDevice = false, canEnterRecoveryKey = true), - aChooseSelfVerificationModeState(isLastDevice = false, canEnterRecoveryKey = false), + aChooseSelfVerificationModeState(canUseAnotherDevice = false, canEnterRecoveryKey = true), + aChooseSelfVerificationModeState(canUseAnotherDevice = false, canEnterRecoveryKey = false), + aChooseSelfVerificationModeState(canUseAnotherDevice = true, canEnterRecoveryKey = true), + aChooseSelfVerificationModeState(canUseAnotherDevice = true, canEnterRecoveryKey = false), ) } fun aChooseSelfVerificationModeState( - isLastDevice: Boolean = false, + canUseAnotherDevice: Boolean = true, canEnterRecoveryKey: Boolean = true, ) = ChooseSelfVerificationModeState( - isLastDevice = isLastDevice, + canUseAnotherDevice = canUseAnotherDevice, canEnterRecoveryKey = canEnterRecoveryKey, directLogoutState = aDirectLogoutState(), eventSink = {}, diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeView.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeView.kt index 74eed0115c..b07c04ac9c 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeView.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeView.kt @@ -76,7 +76,7 @@ fun ChooseSelfVerificationModeView( ButtonColumnMolecule( modifier = Modifier.padding(bottom = 16.dp) ) { - if (state.isLastDevice.not()) { + if (state.canUseAnotherDevice) { Button( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.screen_identity_use_another_device), 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 3801001ed4..3dbf5a6932 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 @@ -24,15 +24,15 @@ class ChooseSessionVerificationModePresenterTest { @Test fun `initial state - is relayed from EncryptionService`() = runTest { val encryptionService = FakeEncryptionService().apply { - // Is last device - emitIsLastDevice(true) + // Has device to verify against + emitHasDevicesToVerifyAgainst(false) // Can enter recovery key emitRecoveryState(RecoveryState.INCOMPLETE) } val presenter = createPresenter(encryptionService = encryptionService) presenter.test { awaitItem().run { - assertThat(isLastDevice).isTrue() + assertThat(canUseAnotherDevice).isFalse() assertThat(canEnterRecoveryKey).isTrue() assertThat(directLogoutState.logoutAction.isUninitialized()).isTrue() } diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSessionVerificationModeViewTest.kt b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSessionVerificationModeViewTest.kt index b89e3e42bd..ed7d99dd19 100644 --- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSessionVerificationModeViewTest.kt +++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSessionVerificationModeViewTest.kt @@ -43,7 +43,7 @@ class ChooseSessionVerificationModeViewTest { fun `clicking on use another device calls the callback`() { ensureCalledOnce { callback -> rule.setChooseSelfVerificationModeView( - aChooseSelfVerificationModeState(isLastDevice = false), + aChooseSelfVerificationModeState(canUseAnotherDevice = true), onUseAnotherDevice = callback, ) rule.clickOn(R.string.screen_identity_use_another_device) diff --git a/features/home/impl/build.gradle.kts b/features/home/impl/build.gradle.kts index b6a83775fc..9a29532ef0 100644 --- a/features/home/impl/build.gradle.kts +++ b/features/home/impl/build.gradle.kts @@ -45,6 +45,7 @@ dependencies { implementation(projects.libraries.permissions.noop) implementation(projects.libraries.preferences.api) implementation(projects.libraries.push.api) + implementation(projects.features.announcement.api) implementation(projects.features.invite.api) implementation(projects.features.networkmonitor.api) implementation(projects.features.logout.api) @@ -60,6 +61,7 @@ dependencies { api(projects.features.home.api) testCommonDependencies(libs, true) + testImplementation(projects.features.announcement.test) testImplementation(projects.features.invite.test) testImplementation(projects.features.logout.test) testImplementation(projects.features.networkmonitor.test) @@ -71,6 +73,7 @@ dependencies { testImplementation(projects.libraries.permissions.noop) testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.preferences.test) + testImplementation(projects.libraries.sessionStorage.test) testImplementation(projects.libraries.push.test) testImplementation(projects.services.analytics.test) testImplementation(projects.services.toolbox.test) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/CurrentUserWithNeighborsBuilder.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/CurrentUserWithNeighborsBuilder.kt new file mode 100644 index 0000000000..9c29766067 --- /dev/null +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/CurrentUserWithNeighborsBuilder.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2025 New Vector 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.home.impl + +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.sessionstorage.api.SessionData +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList + +class CurrentUserWithNeighborsBuilder { + /** + * Build a list of [MatrixUser] containing the current user. If there are other sessions, the list + * will contain 3 users, with the current user in the middle. + * If there is only one other session, the list will contain twice the other user, to allow cycling. + */ + fun build( + matrixUser: MatrixUser, + sessions: List, + ): ImmutableList { + // Sort by position to always have the same order (not depending on last account usage) + return sessions.sortedBy { it.position } + .map { + if (it.userId == matrixUser.userId.value) { + // Always use the freshest profile for the current user + matrixUser + } else { + // Use the data from the DB + MatrixUser( + userId = UserId(it.userId), + displayName = it.userDisplayName, + avatarUrl = it.userAvatarUrl, + ) + } + } + .let { sessionList -> + // If the list has one item, there is no other session, return the list + when (sessionList.size) { + // Can happen when the user signs out (?) + 0 -> listOf(matrixUser) + 1 -> sessionList + else -> { + // Create a list with extra item at the start and end if necessary to have the current user in the middle + // If the list is [A, B, C, D] and the current user is A we want to return [D, A, B] + // If the current user is B, we want to return [A, B, C] + // If the current user is C, we want to return [B, C, D] + // If the current user is D, we want to return [C, D, A] + // Special case: if there are only two users, we want to return [B, A, B] or [A, B, A] to allows cycling + // between the two users. + val currentUserIndex = sessionList.indexOfFirst { it.userId == matrixUser.userId } + when (currentUserIndex) { + // This can happen when the user signs out. + // In this case, just return a singleton list with the current user. + -1 -> listOf(matrixUser) + 0 -> listOf(sessionList.last()) + sessionList.take(2) + sessionList.lastIndex -> sessionList.takeLast(2) + sessionList.first() + else -> sessionList.slice(currentUserIndex - 1..currentUserIndex + 1) + } + } + } + } + .toImmutableList() + } +} diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeEvents.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeEvents.kt index 4632e40d5a..bc0f821845 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeEvents.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeEvents.kt @@ -7,6 +7,9 @@ package io.element.android.features.home.impl +import io.element.android.libraries.matrix.api.core.SessionId + sealed interface HomeEvents { data class SelectHomeNavigationBarItem(val item: HomeNavigationBarItem) : HomeEvents + data class SwitchToAccount(val sessionId: SessionId) : HomeEvents } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt index d8ecf7016f..94f243b634 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt @@ -26,7 +26,7 @@ import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint @@ -56,7 +56,7 @@ import kotlinx.coroutines.withContext import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class HomeFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt index 90565de292..e3ca9612d1 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt @@ -14,9 +14,12 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Inject +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.announcement.api.AnnouncementService import io.element.android.features.home.impl.roomlist.RoomListState import io.element.android.features.home.impl.spaces.HomeSpacesState import io.element.android.features.logout.api.direct.DirectLogoutState @@ -29,6 +32,10 @@ import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.indicator.api.IndicatorService import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.sync.SyncService +import io.element.android.libraries.sessionstorage.api.SessionStore +import kotlinx.collections.immutable.persistentListOf +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.launch @Inject class HomePresenter( @@ -41,10 +48,22 @@ class HomePresenter( private val logoutPresenter: Presenter, private val rageshakeFeatureAvailability: RageshakeFeatureAvailability, private val featureFlagService: FeatureFlagService, + private val sessionStore: SessionStore, + private val announcementService: AnnouncementService, ) : Presenter { + private val currentUserWithNeighborsBuilder = CurrentUserWithNeighborsBuilder() + @Composable override fun present(): HomeState { - val matrixUser = client.userProfile.collectAsState() + val coroutineState = rememberCoroutineScope() + val matrixUser by client.userProfile.collectAsState() + val currentUserAndNeighbors by remember { + combine( + client.userProfile, + sessionStore.sessionsFlow(), + currentUserWithNeighborsBuilder::build, + ) + }.collectAsState(initial = persistentListOf(matrixUser)) val isOnline by syncService.isOnline.collectAsState() val canReportBug by remember { rageshakeFeatureAvailability.isAvailable() }.collectAsState(false) val roomListState = roomListPresenter.present() @@ -68,9 +87,15 @@ class HomePresenter( fun handleEvents(event: HomeEvents) { when (event) { - is HomeEvents.SelectHomeNavigationBarItem -> { + is HomeEvents.SelectHomeNavigationBarItem -> coroutineState.launch { + if (event.item == HomeNavigationBarItem.Spaces) { + announcementService.showAnnouncement(Announcement.Space) + } currentHomeNavigationBarItemOrdinal = event.item.ordinal } + is HomeEvents.SwitchToAccount -> coroutineState.launch { + sessionStore.setLatestSession(event.sessionId.value) + } } } @@ -82,7 +107,7 @@ class HomePresenter( } val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() return HomeState( - matrixUser = matrixUser.value, + currentUserAndNeighbors = currentUserAndNeighbors, showAvatarIndicator = showAvatarIndicator, hasNetworkConnection = isOnline, currentHomeNavigationBarItem = currentHomeNavigationBarItem, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeState.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeState.kt index c4fe0ce0fe..d35412734f 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeState.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeState.kt @@ -13,10 +13,15 @@ import io.element.android.features.home.impl.spaces.HomeSpacesState import io.element.android.features.logout.api.direct.DirectLogoutState import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.matrix.api.user.MatrixUser +import kotlinx.collections.immutable.ImmutableList @Immutable data class HomeState( - val matrixUser: MatrixUser, + /** + * The current user of this session, in case of multiple accounts, will contains 3 items, with the + * current user in the middle. + */ + val currentUserAndNeighbors: ImmutableList, val showAvatarIndicator: Boolean, val hasNetworkConnection: Boolean, val currentHomeNavigationBarItem: HomeNavigationBarItem, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStateProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStateProvider.kt index 59c8c3c500..c5bb339661 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStateProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStateProvider.kt @@ -21,6 +21,7 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.toImmutableList open class HomeStateProvider : PreviewParameterProvider { override val values: Sequence @@ -50,6 +51,7 @@ open class HomeStateProvider : PreviewParameterProvider { internal fun aHomeState( matrixUser: MatrixUser = MatrixUser(userId = UserId("@id:domain"), displayName = "User#1"), + currentUserAndNeighbors: List = listOf(matrixUser), showAvatarIndicator: Boolean = false, hasNetworkConnection: Boolean = true, snackbarMessage: SnackbarMessage? = null, @@ -61,7 +63,7 @@ internal fun aHomeState( directLogoutState: DirectLogoutState = aDirectLogoutState(), eventSink: (HomeEvents) -> Unit = {} ) = HomeState( - matrixUser = matrixUser, + currentUserAndNeighbors = currentUserAndNeighbors.toImmutableList(), showAvatarIndicator = showAvatarIndicator, hasNetworkConnection = hasNetworkConnection, snackbarMessage = snackbarMessage, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt index 37727712fb..aa4742f074 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt @@ -171,12 +171,15 @@ private fun HomeScaffold( topBar = { RoomListTopBar( title = stringResource(state.currentHomeNavigationBarItem.labelRes), - matrixUser = state.matrixUser, + currentUserAndNeighbors = state.currentUserAndNeighbors, showAvatarIndicator = state.showAvatarIndicator, areSearchResultsDisplayed = roomListState.searchState.isSearchActive, onToggleSearch = { roomListState.eventSink(RoomListEvents.ToggleSearchResults) }, onMenuActionClick = onMenuActionClick, onOpenSettings = onOpenSettings, + onAccountSwitch = { + state.eventSink(HomeEvents.SwitchToAccount(it)) + }, scrollBehavior = scrollBehavior, displayMenuItems = state.displayActions, displayFilters = roomListState.displayFilters && state.currentHomeNavigationBarItem == HomeNavigationBarItem.Chats, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/NewNotificationSoundBanner.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/NewNotificationSoundBanner.kt new file mode 100644 index 0000000000..f7516e41e8 --- /dev/null +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/NewNotificationSoundBanner.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2025 New Vector 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.home.impl.components + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import io.element.android.features.home.impl.R +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.ui.strings.CommonStrings + +@Composable +internal fun NewNotificationSoundBanner( + onDismissClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Announcement( + modifier = modifier.roomListBannerPadding(), + title = stringResource(R.string.banner_new_sound_title), + description = stringResource(R.string.banner_new_sound_message), + type = AnnouncementType.Actionable( + actionText = stringResource(CommonStrings.action_ok), + onActionClick = onDismissClick, + onDismissClick = onDismissClick, + ), + ) +} + +@PreviewsDayNight +@Composable +internal fun NewNotificationSoundBannerPreview() = ElementPreview { + NewNotificationSoundBanner( + onDismissClick = {}, + ) +} diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt index 2845a79b8e..34d036b204 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt @@ -251,6 +251,12 @@ private fun RoomsViewList( item { BatteryOptimizationBanner(state = state.batteryOptimizationState) } + } else if (state.showNewNotificationSoundBanner) { + item { + NewNotificationSoundBanner( + onDismissClick = { updatedEventSink(RoomListEvents.DismissNewNotificationSoundBanner) }, + ) + } } } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListTopBar.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListTopBar.kt index f1f06afe6d..abd6e7892d 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListTopBar.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListTopBar.kt @@ -11,19 +11,25 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.pager.VerticalPager +import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha @@ -41,7 +47,6 @@ import io.element.android.features.home.impl.filters.RoomListFiltersView import io.element.android.features.home.impl.filters.aRoomListFiltersState import io.element.android.libraries.designsystem.atomic.atoms.RedIndicatorAtom import io.element.android.libraries.designsystem.components.avatar.Avatar -import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.modifiers.backgroundVerticalGradient @@ -57,23 +62,29 @@ 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.MediumTopAppBar import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.ui.components.aMatrixUserList import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList @OptIn(ExperimentalMaterial3Api::class) @Composable fun RoomListTopBar( title: String, - matrixUser: MatrixUser, + currentUserAndNeighbors: ImmutableList, showAvatarIndicator: Boolean, areSearchResultsDisplayed: Boolean, onToggleSearch: () -> Unit, onMenuActionClick: (RoomListMenuAction) -> Unit, onOpenSettings: () -> Unit, + onAccountSwitch: (SessionId) -> Unit, scrollBehavior: TopAppBarScrollBehavior, displayMenuItems: Boolean, displayFilters: Boolean, @@ -83,10 +94,11 @@ fun RoomListTopBar( ) { DefaultRoomListTopBar( title = title, - matrixUser = matrixUser, + currentUserAndNeighbors = currentUserAndNeighbors, showAvatarIndicator = showAvatarIndicator, areSearchResultsDisplayed = areSearchResultsDisplayed, onOpenSettings = onOpenSettings, + onAccountSwitch = onAccountSwitch, onSearchClick = onToggleSearch, onMenuActionClick = onMenuActionClick, scrollBehavior = scrollBehavior, @@ -102,11 +114,12 @@ fun RoomListTopBar( @Composable private fun DefaultRoomListTopBar( title: String, - matrixUser: MatrixUser, + currentUserAndNeighbors: ImmutableList, showAvatarIndicator: Boolean, areSearchResultsDisplayed: Boolean, scrollBehavior: TopAppBarScrollBehavior, onOpenSettings: () -> Unit, + onAccountSwitch: (SessionId) -> Unit, onSearchClick: () -> Unit, onMenuActionClick: (RoomListMenuAction) -> Unit, displayMenuItems: Boolean, @@ -116,12 +129,6 @@ private fun DefaultRoomListTopBar( modifier: Modifier = Modifier, ) { val collapsedFraction = scrollBehavior.state.collapsedFraction - val avatarData by remember(matrixUser) { - derivedStateOf { - matrixUser.getAvatarData(size = AvatarSize.CurrentUserTopBar) - } - } - Box(modifier = modifier) { val collapsedTitleTextStyle = ElementTheme.typography.aliasScreenTitle val expandedTitleTextStyle = ElementTheme.typography.fontHeadingLgBold.copy( @@ -158,8 +165,9 @@ private fun DefaultRoomListTopBar( }, navigationIcon = { NavigationIcon( - avatarData = avatarData, + currentUserAndNeighbors = currentUserAndNeighbors, showAvatarIndicator = showAvatarIndicator, + onAccountSwitch = onAccountSwitch, onClick = onOpenSettings, ) }, @@ -247,19 +255,67 @@ private fun DefaultRoomListTopBar( @Composable private fun NavigationIcon( - avatarData: AvatarData, + currentUserAndNeighbors: ImmutableList, + showAvatarIndicator: Boolean, + onAccountSwitch: (SessionId) -> Unit, + onClick: () -> Unit, +) { + if (currentUserAndNeighbors.size == 1) { + AccountIcon( + matrixUser = currentUserAndNeighbors.single(), + isCurrentAccount = true, + showAvatarIndicator = showAvatarIndicator, + onClick = onClick, + ) + } else { + // Render a vertical pager + val pagerState = rememberPagerState(initialPage = 1) { currentUserAndNeighbors.size } + // Listen to page changes and switch account if needed + val latestOnAccountSwitch by rememberUpdatedState(onAccountSwitch) + LaunchedEffect(pagerState) { + snapshotFlow { pagerState.settledPage }.collect { page -> + latestOnAccountSwitch(SessionId(currentUserAndNeighbors[page].userId.value)) + } + } + VerticalPager( + state = pagerState, + modifier = Modifier.height(48.dp), + ) { page -> + AccountIcon( + matrixUser = currentUserAndNeighbors[page], + isCurrentAccount = page == 1, + showAvatarIndicator = page == 1 && showAvatarIndicator, + onClick = if (page == 1) { + onClick + } else { + {} + }, + ) + } + } +} + +@Composable +private fun AccountIcon( + matrixUser: MatrixUser, + isCurrentAccount: Boolean, showAvatarIndicator: Boolean, onClick: () -> Unit, ) { IconButton( - modifier = Modifier.testTag(TestTags.homeScreenSettings), + modifier = if (isCurrentAccount) Modifier.testTag(TestTags.homeScreenSettings) else Modifier, onClick = onClick, ) { Box { + val avatarData by remember(matrixUser) { + derivedStateOf { + matrixUser.getAvatarData(size = AvatarSize.CurrentUserTopBar) + } + } Avatar( avatarData = avatarData, avatarType = AvatarType.User, - contentDescription = stringResource(CommonStrings.common_settings), + contentDescription = if (isCurrentAccount) stringResource(CommonStrings.common_settings) else null, ) if (showAvatarIndicator) { RedIndicatorAtom( @@ -276,11 +332,12 @@ private fun NavigationIcon( internal fun DefaultRoomListTopBarPreview() = ElementPreview { DefaultRoomListTopBar( title = stringResource(R.string.screen_roomlist_main_space_title), - matrixUser = MatrixUser(UserId("@id:domain"), "Alice"), + currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")), showAvatarIndicator = false, areSearchResultsDisplayed = false, scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()), onOpenSettings = {}, + onAccountSwitch = {}, onSearchClick = {}, displayMenuItems = true, displayFilters = true, @@ -296,11 +353,33 @@ internal fun DefaultRoomListTopBarPreview() = ElementPreview { internal fun DefaultRoomListTopBarWithIndicatorPreview() = ElementPreview { DefaultRoomListTopBar( title = stringResource(R.string.screen_roomlist_main_space_title), - matrixUser = MatrixUser(UserId("@id:domain"), "Alice"), + currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")), showAvatarIndicator = true, areSearchResultsDisplayed = false, scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()), onOpenSettings = {}, + onAccountSwitch = {}, + onSearchClick = {}, + displayMenuItems = true, + displayFilters = true, + filtersState = aRoomListFiltersState(), + canReportBug = true, + onMenuActionClick = {}, + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@PreviewsDayNight +@Composable +internal fun DefaultRoomListTopBarMultiAccountPreview() = ElementPreview { + DefaultRoomListTopBar( + title = stringResource(R.string.screen_roomlist_main_space_title), + currentUserAndNeighbors = aMatrixUserList().take(3).toImmutableList(), + showAvatarIndicator = false, + areSearchResultsDisplayed = false, + scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()), + onOpenSettings = {}, + onAccountSwitch = {}, onSearchClick = {}, displayMenuItems = true, displayFilters = true, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt index 3036865eea..c5e1798baa 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt @@ -189,10 +189,14 @@ private fun RoomSummaryScaffoldRow( ) { Avatar( avatarData = room.avatarData, - avatarType = AvatarType.Room( - heroes = room.heroes, - isTombstoned = room.isTombstoned, - ), + avatarType = if (room.isSpace) { + AvatarType.Space(isTombstoned = room.isTombstoned) + } else { + AvatarType.Room( + heroes = room.heroes, + isTombstoned = room.isTombstoned, + ) + }, hideImage = hideAvatarImage, ) Spacer(modifier = Modifier.width(16.dp)) 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 b6f908fd5d..ffd6f640ac 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 @@ -69,6 +69,7 @@ class RoomListRoomSummaryFactory( user.getAvatarData(size = AvatarSize.RoomListItem) }.toImmutableList(), isTombstoned = roomInfo.successorRoom != null, + isSpace = roomInfo.isSpace, ) } } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenter.kt index 07d2de96a1..08c34d60ff 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenter.kt @@ -14,7 +14,7 @@ import dev.zacsweers.metro.Inject import io.element.android.features.home.impl.filters.selection.FilterSelectionStrategy import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.roomlist.RoomListService -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.map import io.element.android.libraries.matrix.api.roomlist.RoomListFilter as MatrixRoomListFilter @@ -23,7 +23,7 @@ class RoomListFiltersPresenter( private val roomListService: RoomListService, private val filterSelectionStrategy: FilterSelectionStrategy, ) : Presenter { - private val initialFilters = filterSelectionStrategy.filterSelectionStates.value.toPersistentList() + private val initialFilters = filterSelectionStrategy.filterSelectionStates.value.toImmutableList() @Composable override fun present(): RoomListFiltersState { @@ -41,7 +41,7 @@ class RoomListFiltersPresenter( val filters by produceState(initialValue = initialFilters) { filterSelectionStrategy.filterSelectionStates .map { filters -> - value = filters.toPersistentList() + value = filters.toImmutableList() filters.mapNotNull { filterState -> if (!filterState.isSelected) { return@mapNotNull null diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersState.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersState.kt index bb4146ce84..215641c5b2 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersState.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersState.kt @@ -9,7 +9,7 @@ package io.element.android.features.home.impl.filters import io.element.android.features.home.impl.filters.selection.FilterSelectionState import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList data class RoomListFiltersState( val filterSelectionStates: ImmutableList, @@ -21,6 +21,6 @@ data class RoomListFiltersState( return filterSelectionStates .filter { it.isSelected } .map { it.filter } - .toPersistentList() + .toImmutableList() } } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummary.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummary.kt index 3f166a66b4..8af359d3e5 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummary.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummary.kt @@ -38,6 +38,7 @@ data class RoomListRoomSummary( val inviteSender: InviteSender?, val isTombstoned: Boolean, val heroes: ImmutableList, + val isSpace: Boolean, ) { val isHighlighted = userDefinedNotificationMode != RoomNotificationMode.MUTE && (numberOfUnreadNotifications > 0 || numberOfUnreadMentions > 0) || diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummaryProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummaryProvider.kt index f06e5a1a27..1e763843af 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummaryProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummaryProvider.kt @@ -102,6 +102,15 @@ open class RoomListRoomSummaryProvider : PreviewParameterProvider = emptyList(), isTombstoned: Boolean = false, + isSpace: Boolean = false, ) = RoomListRoomSummary( id = id, roomId = RoomId(id), @@ -172,4 +182,5 @@ internal fun aRoomListRoomSummary( canonicalAlias = canonicalAlias, heroes = heroes.toImmutableList(), isTombstoned = isTombstoned, + isSpace = isSpace ) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContentStateProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContentStateProvider.kt index a421c239cb..86ab5cd39c 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContentStateProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContentStateProvider.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.push.api.battery.BatteryOptimizationState import io.element.android.libraries.push.api.battery.aBatteryOptimizationState import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentSet +import kotlinx.collections.immutable.toImmutableSet open class RoomListContentStateProvider : PreviewParameterProvider { override val values: Sequence @@ -26,21 +26,26 @@ open class RoomListContentStateProvider : PreviewParameterProvider = aRoomListRoomSummaryList(), fullScreenIntentPermissionsState: FullScreenIntentPermissionsState = aFullScreenIntentPermissionsState(), batteryOptimizationState: BatteryOptimizationState = aBatteryOptimizationState(), seenRoomInvites: Set = emptySet(), ) = RoomListContentState.Rooms( securityBannerState = securityBannerState, + showNewNotificationSoundBanner = showNewNotificationSoundBanner, fullScreenIntentPermissionsState = fullScreenIntentPermissionsState, batteryOptimizationState = batteryOptimizationState, summaries = summaries, - seenRoomInvites = seenRoomInvites.toPersistentSet(), + seenRoomInvites = seenRoomInvites.toImmutableSet(), ) internal fun aSkeletonContentState() = RoomListContentState.Skeleton(16) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListEvents.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListEvents.kt index 02df2cac35..52da613be7 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListEvents.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListEvents.kt @@ -14,6 +14,7 @@ sealed interface RoomListEvents { data class UpdateVisibleRange(val range: IntRange) : RoomListEvents data object DismissRequestVerificationPrompt : RoomListEvents data object DismissBanner : RoomListEvents + data object DismissNewNotificationSoundBanner : RoomListEvents data object ToggleSearchResults : RoomListEvents data class ShowContextMenu(val roomSummary: RoomListRoomSummary) : RoomListEvents 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 b8e299c5e9..65d4eb297d 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 @@ -24,6 +24,8 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import dev.zacsweers.metro.Inject import im.vector.app.features.analytics.plan.Interaction +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.announcement.api.AnnouncementService import io.element.android.features.home.impl.datasource.RoomListDataSource import io.element.android.features.home.impl.filters.RoomListFiltersState import io.element.android.features.home.impl.search.RoomListSearchEvents @@ -39,7 +41,6 @@ import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.api.timeline.ReceiptType @@ -50,8 +51,8 @@ import io.element.android.libraries.push.api.battery.BatteryOptimizationState import io.element.android.libraries.push.api.notifications.NotificationCleaner import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analyticsproviders.api.trackers.captureInteraction -import kotlinx.collections.immutable.toPersistentList -import kotlinx.collections.immutable.toPersistentSet +import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toImmutableSet import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job @@ -83,8 +84,9 @@ class RoomListPresenter( private val notificationCleaner: NotificationCleaner, private val appPreferencesStore: AppPreferencesStore, private val seenInvitesStore: SeenInvitesStore, + private val announcementService: AnnouncementService, ) : Presenter { - private val encryptionService: EncryptionService = client.encryptionService() + private val encryptionService = client.encryptionService @Composable override fun present(): RoomListState { @@ -99,6 +101,11 @@ class RoomListPresenter( } var securityBannerDismissed by rememberSaveable { mutableStateOf(false) } + val showNewNotificationSoundBanner by remember { + announcementService.announcementsToShowFlow().map { announcements -> + announcements.contains(Announcement.NewNotificationSound) + } + }.collectAsState(false) // Avatar indicator val hideInvitesAvatar by client.rememberHideInvitesAvatar() @@ -113,6 +120,9 @@ class RoomListPresenter( } RoomListEvents.DismissRequestVerificationPrompt -> securityBannerDismissed = true RoomListEvents.DismissBanner -> securityBannerDismissed = true + RoomListEvents.DismissNewNotificationSoundBanner -> coroutineScope.launch { + announcementService.onAnnouncementDismissed(Announcement.NewNotificationSound) + } RoomListEvents.ToggleSearchResults -> searchState.eventSink(RoomListSearchEvents.ToggleSearchVisibility) is RoomListEvents.ShowContextMenu -> { coroutineScope.showContextMenu(event, contextMenu) @@ -142,7 +152,10 @@ class RoomListPresenter( } } - val contentState = roomListContentState(securityBannerDismissed) + val contentState = roomListContentState( + securityBannerDismissed, + showNewNotificationSoundBanner, + ) val canReportRoom by produceState(false) { value = client.canReportRoom() } @@ -198,6 +211,7 @@ class RoomListPresenter( @Composable private fun roomListContentState( securityBannerDismissed: Boolean, + showNewNotificationSoundBanner: Boolean, ): RoomListContentState { val roomSummaries by produceState(initialValue = AsyncData.Loading()) { roomListDataSource.allRooms.collect { value = AsyncData.Success(it) } @@ -216,15 +230,18 @@ class RoomListPresenter( val seenRoomInvites by remember { seenInvitesStore.seenRoomIds() }.collectAsState(emptySet()) val securityBannerState by rememberSecurityBannerState(securityBannerDismissed) return when { - showEmpty -> RoomListContentState.Empty(securityBannerState = securityBannerState) + showEmpty -> RoomListContentState.Empty( + securityBannerState = securityBannerState, + ) showSkeleton -> RoomListContentState.Skeleton(count = 16) else -> { RoomListContentState.Rooms( securityBannerState = securityBannerState, + showNewNotificationSoundBanner = showNewNotificationSoundBanner, fullScreenIntentPermissionsState = fullScreenIntentPermissionsPresenter.present(), batteryOptimizationState = batteryOptimizationPresenter.present(), - summaries = roomSummaries.dataOrNull().orEmpty().toPersistentList(), - seenRoomInvites = seenRoomInvites.toPersistentSet(), + summaries = roomSummaries.dataOrNull().orEmpty().toImmutableList(), + seenRoomInvites = seenRoomInvites.toImmutableSet(), ) } } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListState.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListState.kt index 4a301f0897..80cd6394e8 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListState.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListState.kt @@ -69,6 +69,7 @@ sealed interface RoomListContentState { val securityBannerState: SecurityBannerState, val fullScreenIntentPermissionsState: FullScreenIntentPermissionsState, val batteryOptimizationState: BatteryOptimizationState, + val showNewNotificationSoundBanner: Boolean, val summaries: ImmutableList, val seenRoomInvites: ImmutableSet, ) : RoomListContentState diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateProvider.kt index 55fc8948f6..faa811b920 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListStateProvider.kt @@ -16,18 +16,16 @@ import io.element.android.features.home.impl.model.aRoomListRoomSummary import io.element.android.features.home.impl.model.anInviteSender import io.element.android.features.home.impl.search.RoomListSearchState import io.element.android.features.home.impl.search.aRoomListSearchState -import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState +import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInviteState import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomState -import io.element.android.libraries.architecture.AsyncAction 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.push.api.battery.aBatteryOptimizationState import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList open class RoomListStateProvider : PreviewParameterProvider { override val values: Sequence @@ -76,16 +74,6 @@ internal fun aLeaveRoomState( override val eventSink: (LeaveRoomEvent) -> Unit = eventSink } -internal fun anAcceptDeclineInviteState( - acceptAction: AsyncAction = AsyncAction.Uninitialized, - declineAction: AsyncAction = AsyncAction.Uninitialized, - eventSink: (AcceptDeclineInviteEvents) -> Unit = {} -) = AcceptDeclineInviteState( - acceptAction = acceptAction, - declineAction = declineAction, - eventSink = eventSink, -) - internal fun aRoomListRoomSummaryList(): ImmutableList { return persistentListOf( aRoomListRoomSummary( @@ -134,5 +122,5 @@ internal fun generateRoomListRoomSummaryList( avatarData = AvatarData("!id$index", "${(65 + index % 26).toChar()}", size = AvatarSize.RoomListItem), id = "!roomId$index:domain", ) - }.toPersistentList() + }.toImmutableList() } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchDataSource.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchDataSource.kt index 4cb4104637..ef853bd1cf 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchDataSource.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchDataSource.kt @@ -15,8 +15,8 @@ import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.api.roomlist.RoomListFilter import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.loadAllIncrementally -import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOn @@ -36,11 +36,11 @@ class RoomListSearchDataSource( source = RoomList.Source.All, ) - val roomSummaries: Flow> = roomList.filteredSummaries + val roomSummaries: Flow> = roomList.filteredSummaries .map { roomSummaries -> roomSummaries .map(roomSummaryFactory::create) - .toPersistentList() + .toImmutableList() } .flowOn(coroutineDispatchers.computation) 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 dea6defc0a..8c3e2962ac 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 @@ -17,7 +17,7 @@ import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.ui.safety.rememberHideInvitesAvatar import kotlinx.collections.immutable.persistentSetOf -import kotlinx.collections.immutable.toPersistentSet +import kotlinx.collections.immutable.toImmutableSet import kotlinx.coroutines.flow.map @Inject @@ -30,7 +30,7 @@ class HomeSpacesPresenter( val hideInvitesAvatar by client.rememberHideInvitesAvatar() val spaceRooms by client.spaceService.spaceRoomsFlow.collectAsState(emptyList()) val seenSpaceInvites by remember { - seenInvitesStore.seenRoomIds().map { it.toPersistentSet() } + seenInvitesStore.seenRoomIds().map { it.toImmutableSet() } }.collectAsState(persistentSetOf()) fun handleEvents(event: HomeSpacesEvents) { 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 8b07b9f526..b18e732b42 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 @@ -33,22 +33,17 @@ fun HomeSpacesView( when (space) { CurrentSpace.Root -> { item { - SpaceHeaderRootView( - numberOfSpaces = state.spaceRooms.size, - // TODO - numberOfRooms = 0, - ) + SpaceHeaderRootView(numberOfSpaces = state.spaceRooms.size) } } is CurrentSpace.Space -> item { SpaceHeaderView( avatarData = space.spaceRoom.getAvatarData(AvatarSize.SpaceHeader), - name = space.spaceRoom.name, + name = space.spaceRoom.displayName, topic = space.spaceRoom.topic, - joinRule = space.spaceRoom.joinRule, + visibility = space.spaceRoom.visibility, heroes = space.spaceRoom.heroes.toImmutableList(), numberOfMembers = space.spaceRoom.numJoinedMembers, - numberOfRooms = space.spaceRoom.childrenCount, ) } } @@ -64,7 +59,7 @@ fun HomeSpacesView( }, onLongClick = { // TODO - } + }, ) } } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/SpaceRoomProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/SpaceRoomProvider.kt index 474e08293a..b1b3ac1950 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/SpaceRoomProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/SpaceRoomProvider.kt @@ -30,7 +30,6 @@ class SpaceRoomProvider : PreviewParameterProvider { roomId = RoomId("!spaceId1:example.com"), ), aSpaceRoom( - name = null, numJoinedMembers = 5, childrenCount = 10, worldReadable = true, @@ -38,14 +37,5 @@ class SpaceRoomProvider : PreviewParameterProvider { roomId = RoomId("!spaceId2:example.com"), state = CurrentUserMembership.INVITED, ), - aSpaceRoom( - name = null, - numJoinedMembers = 5, - childrenCount = 10, - worldReadable = true, - avatarUrl = "anUrl", - roomId = RoomId("!spaceId3:example.com"), - state = CurrentUserMembership.INVITED, - ), ) } diff --git a/features/home/impl/src/main/res/values-bg/translations.xml b/features/home/impl/src/main/res/values-bg/translations.xml index b2053200fc..f988aee0fd 100644 --- a/features/home/impl/src/main/res/values-bg/translations.xml +++ b/features/home/impl/src/main/res/values-bg/translations.xml @@ -6,8 +6,12 @@ "Всички чатове" "Сигурни ли сте, че искате да отхвърлите поканата за присъединяване в %1$s?" "Отказване на покана" + "Сигурни ли сте, че искате да откажете този личен чат с %1$s?" + "Отказване на чат" "Няма покани" "%1$s (%2$s) ви покани" + "Това е еднократен процес, благодаря, че изчакахте." + "Настройване на вашия акаунт." "Създаване на нов разговор или стая" "Започнете, като изпратите съобщение на някого." "Все още няма чатове." diff --git a/features/home/impl/src/main/res/values-da/translations.xml b/features/home/impl/src/main/res/values-da/translations.xml index b5cc2ef7cb..b686fb6e03 100644 --- a/features/home/impl/src/main/res/values-da/translations.xml +++ b/features/home/impl/src/main/res/values-da/translations.xml @@ -3,6 +3,8 @@ "Deaktiver batterioptimering for denne app for at sikre, at alle notifikationer dukker op." "Deaktivér optimering" "Modtager du ikke notifikationer?" + "Dit notifikationsping er blevet opdateret – tydeligere, hurtigere og mindre forstyrrende." + "Vi har opdateret dine lyde" "Gendan din kryptografiske identitet og meddelelseshistorik med en gendannelsesnøgle, hvis du har mistet alle dine eksisterende enheder." "Opsæt gendannelse" "Konfigurer gendannelse for at beskytte din konto" diff --git a/features/home/impl/src/main/res/values-de/translations.xml b/features/home/impl/src/main/res/values-de/translations.xml index 0596dc45e4..2502df5fc1 100644 --- a/features/home/impl/src/main/res/values-de/translations.xml +++ b/features/home/impl/src/main/res/values-de/translations.xml @@ -3,6 +3,8 @@ "Deaktiviere die Batterieoptimierung für diese App, um sicherzustellen, dass alle Benachrichtigungen empfangen werden." "Optimierung deaktivieren" "Kommen die Benachrichtigungen nicht an?" + "Dein Benachrichtigungs-Ping wurde aktualisiert – klarer, schneller und weniger störend." + "Wir haben deine Sounds aktualisiert" "Stelle Deine kryptographische Identität und Deinen Nachrichtenverlauf mit Hilfe eines Wiederherstellungsschlüssels wieder her, falls du alle deine Geräte verloren haben solltest" "Wiederherstellung einrichten" "Wiederherstellung einrichten" diff --git a/features/home/impl/src/main/res/values-eo/translations.xml b/features/home/impl/src/main/res/values-eo/translations.xml index 0dc9b8e816..46986026aa 100644 --- a/features/home/impl/src/main/res/values-eo/translations.xml +++ b/features/home/impl/src/main/res/values-eo/translations.xml @@ -7,5 +7,5 @@ "Enter your backup password" "Forgot your backup password?" "Your message backup is out of sync" - "Looks like you\'re using a new device. Confirm it with another connected device to access your encrypted messages." + "Looks like you\'re using a new device. Confirm it with another linked device to access your encrypted messages." diff --git a/features/home/impl/src/main/res/values-fi/translations.xml b/features/home/impl/src/main/res/values-fi/translations.xml index 31b5ff0a1a..fa7150b546 100644 --- a/features/home/impl/src/main/res/values-fi/translations.xml +++ b/features/home/impl/src/main/res/values-fi/translations.xml @@ -3,6 +3,8 @@ "Ota tämän sovelluksen akunkäytön optimointi pois käytöstä varmistaaksesi, että kaikki ilmoitukset tulevat perille." "Ota optimointi pois käytöstä" "Eikö ilmoitukset tule perille?" + "Ilmoitusääni on päivitetty — selkeämpi, nopeampi ja vähemmän häiritsevä." + "Olemme päivittäneet äänesi" "Palauta kryptografinen identiteettisi ja viestihistoriasi palautusavaimella, mikäli menetät pääsyn kaikkiin laitteisiisi." "Ota palautus käyttöön" "Ota palautus käyttöön tilisi suojaamiseksi" diff --git a/features/home/impl/src/main/res/values-fr/translations.xml b/features/home/impl/src/main/res/values-fr/translations.xml index 11f842916f..d54e8fb943 100644 --- a/features/home/impl/src/main/res/values-fr/translations.xml +++ b/features/home/impl/src/main/res/values-fr/translations.xml @@ -3,6 +3,8 @@ "Désactivez l’optimisation de la batterie pour cette application afin de vous assurer que toutes les notifications sont reçues." "Désactiver l’optimisation" "Ils vous manque des notifications?" + "Le son des notifications a été modifié: plus clair, plus court et moins perturbateur." + "Nous avons rafraîchi les sons" "Générez une nouvelle clé de récupération qui peut être utilisée pour restaurer l’historique de vos messages chiffrés au cas où vous perdriez l’accès à vos appareils." "Configurer la sauvegarde" "Configurer la récupération" diff --git a/features/home/impl/src/main/res/values-hu/translations.xml b/features/home/impl/src/main/res/values-hu/translations.xml index 8d498d08a6..7250794095 100644 --- a/features/home/impl/src/main/res/values-hu/translations.xml +++ b/features/home/impl/src/main/res/values-hu/translations.xml @@ -1,6 +1,6 @@ - "Kapcsolja ki az alkalmazás akkumulátor-optimalizálását, hogy biztosan megkapja az összes értesítést." + "Kapcsolja ki az alkalmazás akkumulátoroptimalizálását, hogy biztosan megkapja az összes értesítést." "Optimalizálás letiltása" "Nem érkeznek meg az értesítések?" "Hozzon létre egy új helyreállítási kulcsot, amellyel visszaállíthatja a titkosított üzenetek előzményeit, ha elveszíti az eszközökhöz való hozzáférést." diff --git a/features/home/impl/src/main/res/values-nb/translations.xml b/features/home/impl/src/main/res/values-nb/translations.xml index 198bb7112d..42216442cb 100644 --- a/features/home/impl/src/main/res/values-nb/translations.xml +++ b/features/home/impl/src/main/res/values-nb/translations.xml @@ -3,6 +3,8 @@ "Deaktiver batterioptimalisering for denne appen for å sikre at alle varsler mottas." "Deaktiver optimalisering" "Kommer ikke varslene frem?" + "Varslingssignalet ditt er oppdatert – tydeligere, raskere og mindre forstyrrende." + "Vi har oppdatert lydene dine" "Gjenopprett din kryptografiske identitet og meldingshistorikk med en gjenopprettingsnøkkel hvis du har mistet alle dine brukte enheter." "Konfigurer gjenoppretting" "Konfigurer gjenoppretting for å beskytte kontoen din" @@ -33,6 +35,7 @@ Inntil videre kan du velge bort filtre for å se de andre chattene dine""Invitasjoner" "Du har ingen ventende invitasjoner." "Lav prioritet" + "Du har ingen lavprioriterte chatter ennå" "Du kan velge bort filtre for å se de andre chattene dine" "Du har ikke chatter for dette utvalget" "Personer" diff --git a/features/home/impl/src/main/res/values-ro/translations.xml b/features/home/impl/src/main/res/values-ro/translations.xml index 36a32bf089..e4a80b4fb7 100644 --- a/features/home/impl/src/main/res/values-ro/translations.xml +++ b/features/home/impl/src/main/res/values-ro/translations.xml @@ -3,6 +3,8 @@ "Dezactivați optimizarea bateriei pentru această aplicație, pentru a vă asigura că toate notificările sunt primite." "Dezactivați optimizarea" "Nu primiți notificări?" + "Sunetul pentru notificări a fost actualizat — mai clar, mai rapid și mai puțin perturbatoar." + "Am reîmprospătat sunetele" "Recuperați-vă identitatea criptografică și mesajele anterioare cu o cheie de recuperare dacă ați pierdut toate dispozitivele existente." "Configurați recuperarea" "Configurați recuperarea pentru a vă proteja contul" diff --git a/features/home/impl/src/main/res/values-ru/translations.xml b/features/home/impl/src/main/res/values-ru/translations.xml index 5ff6a3c714..0f77d56db4 100644 --- a/features/home/impl/src/main/res/values-ru/translations.xml +++ b/features/home/impl/src/main/res/values-ru/translations.xml @@ -3,6 +3,8 @@ "Выключите оптимизацию расхода батареи, чтобы убедиться, что все уведомления будут поступать." "Выключить оптимизацию" "Уведомления не поступают?" + "Ваши уведомления были обновлены — теперь они понятнее, быстрее и менее отвлекающие." + "Мы обновили ваши звуки" "Создайте новый ключ восстановления, который можно использовать для восстановления зашифрованной истории сообщений в случае потери доступа к своим устройствам." "Настроить восстановление" "Для защиты вашего аккаунта рекомендуется настроить восстановление" @@ -13,6 +15,7 @@ "Чтобы больше не пропускать важные звонки, разрешите приложению показывать полноэкранные уведомления на заблокированном экране телефона." "Улучшите качество звонков" "Все чаты" + "Пространства" "Вы уверены, что хотите отклонить приглашение в %1$s?" "Отклонить приглашение" "Вы уверены, что хотите отказаться от личного общения с %1$s?" @@ -32,6 +35,7 @@ "Приглашения" "У вас нет отложенных приглашений." "Низкий приоритет" + "У вас пока нет чатов с низким приоритетом." "Вы можете убрать фильтры, чтобы увидеть другие ваши чаты." "У вас нет чатов для этой подборки" "Пользователи" diff --git a/features/home/impl/src/main/res/values/localazy.xml b/features/home/impl/src/main/res/values/localazy.xml index e6e09b5e47..80a263a179 100644 --- a/features/home/impl/src/main/res/values/localazy.xml +++ b/features/home/impl/src/main/res/values/localazy.xml @@ -3,6 +3,8 @@ "Disable battery optimisation for this app, to make sure all notifications are received." "Disable optimisation" "Notifications not arriving?" + "Your notification ping has been updated—clearer, quicker, and less disruptive." + "We’ve refreshed your sounds" "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices." "Set up recovery" "Set up recovery to protect your account" diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/CurrentUserWithNeighborsBuilderTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/CurrentUserWithNeighborsBuilderTest.kt new file mode 100644 index 0000000000..a03c0d0065 --- /dev/null +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/CurrentUserWithNeighborsBuilderTest.kt @@ -0,0 +1,222 @@ +/* + * Copyright 2025 New Vector 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.home.impl + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.A_USER_ID_2 +import io.element.android.libraries.matrix.test.A_USER_ID_3 +import io.element.android.libraries.matrix.ui.components.aMatrixUser +import io.element.android.libraries.sessionstorage.api.SessionData +import io.element.android.libraries.sessionstorage.test.aSessionData +import org.junit.Test + +class CurrentUserWithNeighborsBuilderTest { + @Test + fun `build on empty list returns current user`() { + val sut = CurrentUserWithNeighborsBuilder() + val matrixUser = aMatrixUser() + val list = listOf() + val result = sut.build(matrixUser, list) + assertThat(result).containsExactly(matrixUser) + } + + @Test + fun `ensure that account are sorted by position`() { + val sut = CurrentUserWithNeighborsBuilder() + val matrixUser = aMatrixUser(id = A_USER_ID.value) + val list = listOf( + aSessionData( + sessionId = A_USER_ID.value, + position = 3, + ), + aSessionData( + sessionId = A_USER_ID_2.value, + position = 2, + ), + aSessionData( + sessionId = A_USER_ID_3.value, + position = 1, + ), + ) + val result = sut.build(matrixUser, list) + assertThat(result.map { it.userId }).containsExactly( + A_USER_ID_3, + A_USER_ID_2, + A_USER_ID, + ) + } + + @Test + fun `if current user is not found, return a singleton with current user`() { + val sut = CurrentUserWithNeighborsBuilder() + val matrixUser = aMatrixUser(id = A_USER_ID.value) + val list = listOf( + aSessionData( + sessionId = A_USER_ID_2.value, + ), + aSessionData( + sessionId = A_USER_ID_3.value, + ), + ) + val result = sut.build(matrixUser, list) + assertThat(result.map { it.userId }).containsExactly( + A_USER_ID, + ) + } + + @Test + fun `one account, will return a singleton`() { + val sut = CurrentUserWithNeighborsBuilder() + val matrixUser = aMatrixUser(id = A_USER_ID.value) + val list = listOf( + aSessionData( + sessionId = A_USER_ID.value, + ), + ) + val result = sut.build(matrixUser, list) + assertThat(result.map { it.userId }).containsExactly( + A_USER_ID, + ) + } + + @Test + fun `two accounts, first is current, will return 3 items`() { + val sut = CurrentUserWithNeighborsBuilder() + val matrixUser = aMatrixUser(id = A_USER_ID.value) + val list = listOf( + aSessionData( + sessionId = A_USER_ID.value, + ), + aSessionData( + sessionId = A_USER_ID_2.value, + ), + ) + val result = sut.build(matrixUser, list) + assertThat(result.map { it.userId }).containsExactly( + A_USER_ID_2, + A_USER_ID, + A_USER_ID_2, + ) + } + + @Test + fun `two accounts, second is current, will return 3 items`() { + val sut = CurrentUserWithNeighborsBuilder() + val matrixUser = aMatrixUser(id = A_USER_ID_2.value) + val list = listOf( + aSessionData( + sessionId = A_USER_ID.value, + ), + aSessionData( + sessionId = A_USER_ID_2.value, + ), + ) + val result = sut.build(matrixUser, list) + assertThat(result.map { it.userId }).containsExactly( + A_USER_ID, + A_USER_ID_2, + A_USER_ID, + ) + } + + @Test + fun `three accounts, first is current, will return last current and next`() { + val sut = CurrentUserWithNeighborsBuilder() + val matrixUser = aMatrixUser(id = A_USER_ID.value) + val list = listOf( + aSessionData( + sessionId = A_USER_ID.value, + ), + aSessionData( + sessionId = A_USER_ID_2.value, + ), + aSessionData( + sessionId = A_USER_ID_3.value, + ), + ) + val result = sut.build(matrixUser, list) + assertThat(result.map { it.userId }).containsExactly( + A_USER_ID_3, + A_USER_ID, + A_USER_ID_2, + ) + } + + @Test + fun `three accounts, second is current, will return first current and last`() { + val sut = CurrentUserWithNeighborsBuilder() + val matrixUser = aMatrixUser(id = A_USER_ID_2.value) + val list = listOf( + aSessionData( + sessionId = A_USER_ID.value, + ), + aSessionData( + sessionId = A_USER_ID_2.value, + ), + aSessionData( + sessionId = A_USER_ID_3.value, + ), + ) + val result = sut.build(matrixUser, list) + assertThat(result.map { it.userId }).containsExactly( + A_USER_ID, + A_USER_ID_2, + A_USER_ID_3, + ) + } + + @Test + fun `three accounts, current is last, will return middle, current and first`() { + val sut = CurrentUserWithNeighborsBuilder() + val matrixUser = aMatrixUser(id = A_USER_ID_3.value) + val list = listOf( + aSessionData( + sessionId = A_USER_ID_2.value, + ), + aSessionData( + sessionId = A_USER_ID_3.value, + ), + aSessionData( + sessionId = A_USER_ID.value, + ), + ) + val result = sut.build(matrixUser, list) + assertThat(result.map { it.userId }).containsExactly( + A_USER_ID, + A_USER_ID_2, + A_USER_ID_3, + ) + } + + @Test + fun `one account, will return data from matrix user and not from db`() { + val sut = CurrentUserWithNeighborsBuilder() + val matrixUser = aMatrixUser( + id = A_USER_ID.value, + displayName = "Bob", + avatarUrl = "avatarUrl", + ) + val list = listOf( + aSessionData( + sessionId = A_USER_ID.value, + userDisplayName = "Outdated Bob", + userAvatarUrl = "outdatedAvatarUrl", + ), + ) + val result = sut.build(matrixUser, list) + assertThat(result).containsExactly( + MatrixUser( + userId = A_USER_ID, + displayName = "Bob", + avatarUrl = "avatarUrl", + ) + ) + } +} diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/HomePresenterTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/HomePresenterTest.kt index a84dbd6309..938e0720d7 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/HomePresenterTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/HomePresenterTest.kt @@ -11,11 +11,14 @@ 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.announcement.api.Announcement +import io.element.android.features.announcement.api.AnnouncementService import io.element.android.features.home.impl.roomlist.aRoomListState import io.element.android.features.home.impl.spaces.HomeSpacesState import io.element.android.features.home.impl.spaces.aHomeSpacesState import io.element.android.features.logout.api.direct.aDirectLogoutState import io.element.android.features.rageshake.api.RageshakeFeatureAvailability +import io.element.android.features.rageshake.test.logs.FakeAnnouncementService import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.featureflag.api.FeatureFlagService @@ -31,9 +34,15 @@ import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.sync.FakeSyncService +import io.element.android.libraries.sessionstorage.api.SessionStore +import io.element.android.libraries.sessionstorage.test.InMemorySessionStore +import io.element.android.libraries.sessionstorage.test.aSessionData import io.element.android.tests.testutils.MutablePresenter import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.test import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest @@ -44,6 +53,8 @@ class HomePresenterTest { @get:Rule val warmUpRule = WarmUpRule() + private val isSpaceEnabled = FeatureFlags.Space.defaultValue(aBuildMeta()) + @Test fun `present - should start with no user and then load user with success`() = runTest { val matrixClient = FakeMatrixClient( @@ -54,20 +65,33 @@ class HomePresenterTest { val presenter = createHomePresenter( client = matrixClient, rageshakeFeatureAvailability = { flowOf(false) }, + sessionStore = InMemorySessionStore( + initialList = listOf( + aSessionData( + sessionId = matrixClient.sessionId.value, + userDisplayName = null, + userAvatarUrl = null, + ) + ), + ), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + if (isSpaceEnabled) skipItems(1) val initialState = awaitItem() - assertThat(initialState.matrixUser).isEqualTo(MatrixUser(A_USER_ID)) + assertThat(initialState.currentUserAndNeighbors.first()).isEqualTo( + MatrixUser(A_USER_ID, null, null) + ) assertThat(initialState.canReportBug).isFalse() + skipItems(1) val withUserState = awaitItem() - assertThat(withUserState.matrixUser.userId).isEqualTo(A_USER_ID) - assertThat(withUserState.matrixUser.displayName).isEqualTo(A_USER_NAME) - assertThat(withUserState.matrixUser.avatarUrl).isEqualTo(AN_AVATAR_URL) + assertThat(withUserState.currentUserAndNeighbors.first()).isEqualTo( + MatrixUser(A_USER_ID, A_USER_NAME, AN_AVATAR_URL) + ) assertThat(withUserState.showAvatarIndicator).isFalse() - assertThat(withUserState.isSpaceFeatureEnabled).isFalse() - assertThat(withUserState.showNavigationBar).isFalse() + assertThat(withUserState.isSpaceFeatureEnabled).isEqualTo(isSpaceEnabled) + assertThat(withUserState.showNavigationBar).isEqualTo(isSpaceEnabled) } } @@ -75,6 +99,9 @@ class HomePresenterTest { fun `present - can report bug`() = runTest { val presenter = createHomePresenter( rageshakeFeatureAvailability = { flowOf(true) }, + sessionStore = InMemorySessionStore( + updateUserProfileResult = { _, _, _ -> }, + ), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -92,6 +119,9 @@ class HomePresenterTest { featureFlagService = FakeFeatureFlagService( initialState = mapOf(FeatureFlags.Space.key to true), ), + sessionStore = InMemorySessionStore( + updateUserProfileResult = { _, _, _ -> }, + ), ) presenter.test { skipItems(1) @@ -105,10 +135,14 @@ class HomePresenterTest { val indicatorService = FakeIndicatorService() val presenter = createHomePresenter( indicatorService = indicatorService, + sessionStore = InMemorySessionStore( + updateUserProfileResult = { _, _, _ -> }, + ), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + if (isSpaceEnabled) skipItems(1) val initialState = awaitItem() assertThat(initialState.showAvatarIndicator).isFalse() indicatorService.setShowRoomListTopBarIndicator(true) @@ -124,27 +158,44 @@ class HomePresenterTest { userAvatarUrl = null, ) matrixClient.givenGetProfileResult(matrixClient.sessionId, Result.failure(AN_EXCEPTION)) - val presenter = createHomePresenter(client = matrixClient) + val presenter = createHomePresenter( + client = matrixClient, + sessionStore = InMemorySessionStore( + updateUserProfileResult = { _, _, _ -> }, + ), + ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + if (isSpaceEnabled) skipItems(1) val initialState = awaitItem() - assertThat(initialState.matrixUser).isEqualTo(MatrixUser(matrixClient.sessionId)) + assertThat(initialState.currentUserAndNeighbors.first()).isEqualTo(MatrixUser(matrixClient.sessionId)) // No new state is coming } } @Test fun `present - NavigationBar change`() = runTest { - val presenter = createHomePresenter() + val showAnnouncementResult = lambdaRecorder { } + val presenter = createHomePresenter( + sessionStore = InMemorySessionStore( + updateUserProfileResult = { _, _, _ -> }, + ), + announcementService = FakeAnnouncementService( + showAnnouncementResult = showAnnouncementResult, + ) + ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + if (isSpaceEnabled) skipItems(1) val initialState = awaitItem() assertThat(initialState.currentHomeNavigationBarItem).isEqualTo(HomeNavigationBarItem.Chats) initialState.eventSink(HomeEvents.SelectHomeNavigationBarItem(HomeNavigationBarItem.Spaces)) val finalState = awaitItem() assertThat(finalState.currentHomeNavigationBarItem).isEqualTo(HomeNavigationBarItem.Spaces) + showAnnouncementResult.assertions().isCalledOnce() + .with(value(Announcement.Space)) } } @@ -152,10 +203,16 @@ class HomePresenterTest { fun `present - NavigationBar is hidden when the last space is left`() = runTest { val homeSpacesPresenter = MutablePresenter(aHomeSpacesState()) val presenter = createHomePresenter( + sessionStore = InMemorySessionStore( + updateUserProfileResult = { _, _, _ -> }, + ), featureFlagService = FakeFeatureFlagService( initialState = mapOf(FeatureFlags.Space.key to true), ), homeSpacesPresenter = homeSpacesPresenter, + announcementService = FakeAnnouncementService( + showAnnouncementResult = {}, + ) ) presenter.test { skipItems(1) @@ -185,6 +242,8 @@ internal fun createHomePresenter( indicatorService: IndicatorService = FakeIndicatorService(), featureFlagService: FeatureFlagService = FakeFeatureFlagService(), homeSpacesPresenter: Presenter = Presenter { aHomeSpacesState() }, + sessionStore: SessionStore = InMemorySessionStore(), + announcementService: AnnouncementService = FakeAnnouncementService(), ) = HomePresenter( client = client, syncService = syncService, @@ -195,4 +254,6 @@ internal fun createHomePresenter( homeSpacesPresenter = homeSpacesPresenter, rageshakeFeatureAvailability = rageshakeFeatureAvailability, featureFlagService = featureFlagService, + sessionStore = sessionStore, + announcementService = announcementService, ) diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/model/RoomListBaseRoomSummaryTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/model/RoomListBaseRoomSummaryTest.kt index 55b3f1ffee..b62c19fc7b 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/model/RoomListBaseRoomSummaryTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/model/RoomListBaseRoomSummaryTest.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_NAME -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import org.junit.Test class RoomListBaseRoomSummaryTest { @@ -85,6 +85,7 @@ internal fun createRoomListRoomSummary( heroes: List = emptyList(), timestamp: String? = null, isTombstoned: Boolean = false, + isSpace: Boolean = false, ) = RoomListRoomSummary( id = A_ROOM_ID.value, roomId = A_ROOM_ID, @@ -104,6 +105,7 @@ internal fun createRoomListRoomSummary( canonicalAlias = null, inviteSender = null, isDm = false, - heroes = heroes.toPersistentList(), + heroes = heroes.toImmutableList(), isTombstoned = isTombstoned, + isSpace = isSpace ) 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 044c150ad2..bef312cdea 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 @@ -12,6 +12,8 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.Interaction +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.announcement.api.AnnouncementService import io.element.android.features.home.impl.FakeDateTimeObserver import io.element.android.features.home.impl.datasource.RoomListDataSource import io.element.android.features.home.impl.datasource.aRoomListRoomSummaryFactory @@ -24,9 +26,11 @@ import io.element.android.features.home.impl.search.aRoomListSearchState 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.AcceptDeclineInviteState +import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInviteState import io.element.android.features.invite.test.InMemorySeenInvitesStore import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomState +import io.element.android.features.rageshake.test.logs.FakeAnnouncementService import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.dateformatter.api.DateFormatter import io.element.android.libraries.dateformatter.test.FakeDateFormatter @@ -74,6 +78,7 @@ import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.test import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest @@ -592,6 +597,42 @@ class RoomListPresenterTest { } } + @Test + fun `present - notification sound banner`() = runTest { + val subscribeToVisibleRoomsLambda = lambdaRecorder { _: List -> } + val roomListService = FakeRoomListService(subscribeToVisibleRoomsLambda = subscribeToVisibleRoomsLambda) + val matrixClient = FakeMatrixClient( + roomListService = roomListService, + ) + val roomSummary = aRoomSummary( + currentUserMembership = CurrentUserMembership.INVITED + ) + roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1)) + roomListService.postAllRooms(listOf(roomSummary)) + val onAnnouncementDismissedResult = lambdaRecorder { } + val announcementService = FakeAnnouncementService( + onAnnouncementDismissedResult = onAnnouncementDismissedResult, + ) + val presenter = createRoomListPresenter( + client = matrixClient, + announcementService = announcementService, + ) + presenter.test { + assertThat(announcementService.announcementsToShowFlow().first()).isEmpty() + skipItems(1) + val state = awaitItem() + assertThat(state.contentAsRooms().showNewNotificationSoundBanner).isFalse() + announcementService.emitAnnouncementsToShow(listOf(Announcement.NewNotificationSound)) + assertThat(awaitItem().contentAsRooms().showNewNotificationSoundBanner).isTrue() + state.eventSink(RoomListEvents.DismissNewNotificationSoundBanner) + onAnnouncementDismissedResult.assertions().isCalledOnce() + .with(value(Announcement.NewNotificationSound)) + // Simulate service updating the value + announcementService.emitAnnouncementsToShow(emptyList()) + assertThat(awaitItem().contentAsRooms().showNewNotificationSoundBanner).isFalse() + } + } + private fun TestScope.createRoomListPresenter( client: MatrixClient = FakeMatrixClient(), leaveRoomState: LeaveRoomState = aLeaveRoomState(), @@ -605,6 +646,7 @@ class RoomListPresenterTest { notificationCleaner: NotificationCleaner = FakeNotificationCleaner(), appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(), seenInvitesStore: SeenInvitesStore = InMemorySeenInvitesStore(), + announcementService: AnnouncementService = FakeAnnouncementService(), ) = RoomListPresenter( client = client, leaveRoomPresenter = { leaveRoomState }, @@ -615,7 +657,7 @@ class RoomListPresenterTest { roomLastMessageFormatter = roomLastMessageFormatter, ), coroutineDispatchers = testCoroutineDispatchers(), - notificationSettingsService = client.notificationSettingsService(), + notificationSettingsService = client.notificationSettingsService, sessionCoroutineScope = backgroundScope, dateTimeObserver = FakeDateTimeObserver(), ), @@ -629,5 +671,6 @@ class RoomListPresenterTest { notificationCleaner = notificationCleaner, appPreferencesStore = appPreferencesStore, seenInvitesStore = seenInvitesStore, + announcementService = announcementService, ) } diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/InviteData.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/InviteData.kt index fa296edc1c..9f826ac228 100644 --- a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/InviteData.kt +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/InviteData.kt @@ -41,7 +41,7 @@ fun RoomInfo.toInviteData(): InviteData { fun SpaceRoom.toInviteData(): InviteData { return InviteData( roomId = roomId, - roomName = name ?: roomId.value, + roomName = displayName, isDm = false, ) } diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteStateProvider.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteStateProvider.kt new file mode 100644 index 0000000000..bd5c5e6749 --- /dev/null +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/acceptdecline/AcceptDeclineInviteStateProvider.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2024 New Vector 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.invite.api.acceptdecline + +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.core.RoomId + +fun anAcceptDeclineInviteState( + acceptAction: AsyncAction = AsyncAction.Uninitialized, + declineAction: AsyncAction = AsyncAction.Uninitialized, + eventSink: (AcceptDeclineInviteEvents) -> Unit = {}, +) = AcceptDeclineInviteState( + acceptAction = acceptAction, + declineAction = declineAction, + eventSink = eventSink, +) diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteStateProvider.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteStateProvider.kt index 9896de1d3e..6db000d3db 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteStateProvider.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInviteStateProvider.kt @@ -9,9 +9,9 @@ package io.element.android.features.invite.impl.acceptdecline import androidx.compose.ui.tooling.preview.PreviewParameterProvider 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.AcceptDeclineInviteState import io.element.android.features.invite.api.acceptdecline.ConfirmingDeclineInvite +import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInviteState import io.element.android.features.invite.impl.AcceptInvite import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId @@ -51,13 +51,3 @@ open class AcceptDeclineInviteStateProvider : PreviewParameterProvider = AsyncAction.Uninitialized, - declineAction: AsyncAction = AsyncAction.Uninitialized, - eventSink: (AcceptDeclineInviteEvents) -> Unit = {} -) = AcceptDeclineInviteState( - acceptAction = acceptAction, - declineAction = declineAction, - eventSink = eventSink, -) diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockNode.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockNode.kt index 7264ee4636..51cdf59b6a 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockNode.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockNode.kt @@ -13,7 +13,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.invite.api.InviteData import io.element.android.libraries.architecture.NodeInputs @@ -21,7 +21,7 @@ import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class DeclineAndBlockNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 f9639f69fe..59812a5e04 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 @@ -17,7 +17,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.features.invite.api.InviteData import io.element.android.features.invite.impl.DeclineInvite import io.element.android.libraries.architecture.AsyncAction @@ -28,7 +28,7 @@ import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -@Inject +@AssistedInject class DeclineAndBlockPresenter( @Assisted private val inviteData: InviteData, private val declineInvite: DeclineInvite, diff --git a/features/invite/impl/src/main/res/values-bg/translations.xml b/features/invite/impl/src/main/res/values-bg/translations.xml index 5e3c9a6fbd..e089814fe9 100644 --- a/features/invite/impl/src/main/res/values-bg/translations.xml +++ b/features/invite/impl/src/main/res/values-bg/translations.xml @@ -3,6 +3,9 @@ "Блокиране на потребителя" "Сигурни ли сте, че искате да отхвърлите поканата за присъединяване в %1$s?" "Отказване на покана" + "Сигурни ли сте, че искате да откажете този личен чат с %1$s?" + "Отказване на чат" "Няма покани" "%1$s (%2$s) ви покани" + "Отхвърляне и блокиране" diff --git a/features/invite/impl/src/main/res/values-cs/translations.xml b/features/invite/impl/src/main/res/values-cs/translations.xml index f576269343..28da3dc4b5 100644 --- a/features/invite/impl/src/main/res/values-cs/translations.xml +++ b/features/invite/impl/src/main/res/values-cs/translations.xml @@ -3,7 +3,7 @@ "Od tohoto uživatele neuvidíte žádné zprávy ani pozvánky do místnosti" "Zablokovat uživatele" "Nahlaste tuto místnost svému poskytovateli účtu." - "Popište důvod nahlášení…" + "Popište důvod…" "Odmítnout a zablokovat" "Opravdu chcete odmítnout pozvánku do %1$s?" "Odmítnout pozvání" diff --git a/features/invite/impl/src/main/res/values-cy/translations.xml b/features/invite/impl/src/main/res/values-cy/translations.xml index a84fa2397a..e32337a162 100644 --- a/features/invite/impl/src/main/res/values-cy/translations.xml +++ b/features/invite/impl/src/main/res/values-cy/translations.xml @@ -3,7 +3,7 @@ "Fyddwch chi ddim yn gweld unrhyw negeseuon neu wahoddiadau ystafell gan y defnyddiwr hwn" "Rhwystro defnyddiwr" "Adrodd am yr ystafell hon i ddarparwr eich cyfrif." - "Disgrifiwch y rheswm dros adrodd…" + "Disgrifiwch y rheswm…" "Gwrthod a rhwystro" "Ydych chi\'n siŵr eich bod am wrthod y gwahoddiad i ymuno â %1$s?" "Gwrthod y gwahoddiad" diff --git a/features/invite/impl/src/main/res/values-de/translations.xml b/features/invite/impl/src/main/res/values-de/translations.xml index bb118a99e8..7b6ca2eb61 100644 --- a/features/invite/impl/src/main/res/values-de/translations.xml +++ b/features/invite/impl/src/main/res/values-de/translations.xml @@ -3,7 +3,7 @@ "Du wirst keine Nachrichten oder Chat-Einladungen von diesem Nutzer sehen." "Nutzer blockieren" "Melde diesen Chat deinem Konto-Anbieter." - "Nenne den Grund für die Meldung…" + "Beschreibe den Grund für die Meldung…" "Ablehnen und blockieren" "Möchtest du die Einladung zum Betreten von %1$s wirklich ablehnen?" "Einladung ablehnen" diff --git a/features/invite/impl/src/main/res/values-et/translations.xml b/features/invite/impl/src/main/res/values-et/translations.xml index 91906fa877..1bc1e93b59 100644 --- a/features/invite/impl/src/main/res/values-et/translations.xml +++ b/features/invite/impl/src/main/res/values-et/translations.xml @@ -3,7 +3,7 @@ "Sa ei näe enam selle kasutaja saadetud sõnumeid ja jututubade kutseid" "Blokeeri kasutaja" "Teata sellest jututoast oma teenusepakkujale." - "Kirjelda teatamise põhjust…" + "Kirjelda põhjust…" "Keeldu ja blokeeri" "Kas sa oled kindel, et soovid keelduda liitumiskutsest: %1$s?" "Lükka kutse tagasi" diff --git a/features/invite/impl/src/main/res/values-eu/translations.xml b/features/invite/impl/src/main/res/values-eu/translations.xml index 49a545c4c9..289a8fa418 100644 --- a/features/invite/impl/src/main/res/values-eu/translations.xml +++ b/features/invite/impl/src/main/res/values-eu/translations.xml @@ -9,5 +9,5 @@ "Ez dago gonbidapenik" "%1$s(e)k (%2$s) gonbidatu zaitu" "Eman gonbidapenari ezetza eta blokeatu" - "Eman ezetza eta blokeatu" + "Baztertu eta blokeatu" diff --git a/features/invite/impl/src/main/res/values-fi/translations.xml b/features/invite/impl/src/main/res/values-fi/translations.xml index 47ad368d73..215e2ee31b 100644 --- a/features/invite/impl/src/main/res/values-fi/translations.xml +++ b/features/invite/impl/src/main/res/values-fi/translations.xml @@ -3,7 +3,7 @@ "Et tule näkemään viestejä tai kutsuja tältä käyttäjältä" "Estä käyttäjä" "Ilmoita tästä huoneesta palveluntarjoajallesi." - "Kerro syy ilmoittamiseen…" + "Kuvaile syytä…" "Hylkää ja estä" "Haluatko varmasti hylätä kutsun liittyä %1$s -huoneeseen?" "Hylkää kutsu" diff --git a/features/invite/impl/src/main/res/values-hu/translations.xml b/features/invite/impl/src/main/res/values-hu/translations.xml index 97595ed421..e75d2961ed 100644 --- a/features/invite/impl/src/main/res/values-hu/translations.xml +++ b/features/invite/impl/src/main/res/values-hu/translations.xml @@ -3,8 +3,8 @@ "Ettől a felhasználótól nem fog többé üzeneteket vagy meghívásokat látni." "Felhasználó letiltása" "A szoba jelentése a fiókszolgáltatójának." - "Írja le a jelentés okát…" - "Elutasítás és blokkolás" + "Írja le az okot…" + "Elutasítás és letiltás" "Biztos, hogy elutasítja a meghívást, hogy csatlakozzon ehhez: %1$s?" "Meghívás elutasítása" "Biztos, hogy elutasítja ezt a privát csevegést vele: %1$s?" @@ -14,5 +14,5 @@ "Igen, elutasítás és blokkolás" "Biztos, hogy elutasítja a meghívást, hogy csatlakozzon ehhez a szobához? Ez azt is megakadályozza, hogy %1$s kapcsolatba lépjen Önnel, vagy szobákba hívja." "Meghívó elutasítása és blokkolás" - "Elutasítás és blokkolás" + "Elutasítás és letiltás" diff --git a/features/invite/impl/src/main/res/values-nb/translations.xml b/features/invite/impl/src/main/res/values-nb/translations.xml index 2523466348..31091a1d92 100644 --- a/features/invite/impl/src/main/res/values-nb/translations.xml +++ b/features/invite/impl/src/main/res/values-nb/translations.xml @@ -3,7 +3,7 @@ "Du vil ikke se noen meldinger eller rominvitasjoner fra denne brukeren" "Blokker bruker" "Rapporter dette rommet til din kontoleverandør." - "Beskriv årsaken for å rapportere…" + "Beskriv årsaken…" "Avslå og blokker" "Er du sikker på at du vil takke nei til invitasjonen til å bli med i %1$s?" "Avvis invitasjon" diff --git a/features/invite/impl/src/main/res/values-nl/translations.xml b/features/invite/impl/src/main/res/values-nl/translations.xml index 65051679a2..4abfdda76d 100644 --- a/features/invite/impl/src/main/res/values-nl/translations.xml +++ b/features/invite/impl/src/main/res/values-nl/translations.xml @@ -7,4 +7,5 @@ "Chat weigeren" "Geen uitnodigingen" "%1$s (%2$s) heeft je uitgenodigd" + "Weigeren en blokkeren" diff --git a/features/invite/impl/src/main/res/values-pl/translations.xml b/features/invite/impl/src/main/res/values-pl/translations.xml index e8636fc3dc..b20ee321ea 100644 --- a/features/invite/impl/src/main/res/values-pl/translations.xml +++ b/features/invite/impl/src/main/res/values-pl/translations.xml @@ -3,7 +3,7 @@ "Nie zobaczysz żadnych wiadomości ani zaproszeń od tego użytkownika" "Zablokuj użytkownika" "Zgłoś pokój dostawcy swojego konta." - "Opisz powód zgłoszenia…" + "Opisz powód…" "Odrzuć i zablokuj" "Czy na pewno chcesz odrzucić zaproszenie dołączenia do %1$s?" "Odrzuć zaproszenie" diff --git a/features/invite/impl/src/main/res/values-pt-rBR/translations.xml b/features/invite/impl/src/main/res/values-pt-rBR/translations.xml index f82f98cb1c..bf0ee63061 100644 --- a/features/invite/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/invite/impl/src/main/res/values-pt-rBR/translations.xml @@ -3,7 +3,7 @@ "Você não verá nenhuma mensagem ou convite de sala deste usuário" "Bloquear usuário" "Denuncie esta sala ao fornecedor da sua conta." - "Descreva o motivo da denúncia…" + "Descreva o motivo para denunciar…" "Recusar e bloquear" "Tem certeza de que deseja recusar o convite para entrar em %1$s?" "Recusar convite" diff --git a/features/invite/impl/src/main/res/values-pt/translations.xml b/features/invite/impl/src/main/res/values-pt/translations.xml index 513f14eea6..1bdf28b8f6 100644 --- a/features/invite/impl/src/main/res/values-pt/translations.xml +++ b/features/invite/impl/src/main/res/values-pt/translations.xml @@ -3,7 +3,7 @@ "Não vais ver quaisquer mensagens ou convites para sala deste utilizador" "Bloquear utilizador" "Denunciar esta sala ao fornecedor da tua conta." - "Descreve a razão para bloquear…" + "Descreve a razão para denunciar…" "Rejeitar e bloquear" "Tens a certeza que queres rejeitar o convite para entra em %1$s?" "Rejeitar convite" diff --git a/features/invite/impl/src/main/res/values-ru/translations.xml b/features/invite/impl/src/main/res/values-ru/translations.xml index 90d467e62c..f22b81feb2 100644 --- a/features/invite/impl/src/main/res/values-ru/translations.xml +++ b/features/invite/impl/src/main/res/values-ru/translations.xml @@ -3,7 +3,7 @@ "Вы не увидите сообщений или приглашений в комнату от этого пользователя" "Заблокировать пользователя" "Сообщите об этой комнате своему поставщику учетной записи." - "Опишите причину жалобы…" + "Опишите причину…" "Отклонить и заблокировать" "Вы уверены, что хотите отклонить приглашение в %1$s?" "Отклонить приглашение" diff --git a/features/invite/impl/src/main/res/values-sk/translations.xml b/features/invite/impl/src/main/res/values-sk/translations.xml index a14b5b0dae..826aa023f6 100644 --- a/features/invite/impl/src/main/res/values-sk/translations.xml +++ b/features/invite/impl/src/main/res/values-sk/translations.xml @@ -3,7 +3,7 @@ "Od tohto používateľa sa vám nezobrazia žiadne správy ani pozvánky do miestnosti" "Zablokovať používateľa" "Nahlásiť túto miestnosť poskytovateľovi účtu." - "Opíšte dôvod nahlásenia…" + "Popíšte dôvod…" "Odmietnuť a zablokovať" "Naozaj chcete odmietnuť pozvánku na pripojenie do %1$s?" "Odmietnuť pozvanie" diff --git a/features/invite/impl/src/main/res/values-sv/translations.xml b/features/invite/impl/src/main/res/values-sv/translations.xml index a57336b50e..f44783189a 100644 --- a/features/invite/impl/src/main/res/values-sv/translations.xml +++ b/features/invite/impl/src/main/res/values-sv/translations.xml @@ -3,7 +3,7 @@ "Du kommer inte att se några meddelanden eller rumsinbjudningar från den här användaren" "Blockera användare" "Rapportera det här rummet till din kontoleverantör." - "Beskriv skälet för anmälan …" + "Beskriv anledningen …" "Avvisa och blockera" "Är du säker på att du vill tacka nej till inbjudan att gå med%1$s?" "Avböj inbjudan" diff --git a/features/invite/impl/src/main/res/values-tr/translations.xml b/features/invite/impl/src/main/res/values-tr/translations.xml index 3cfdca3ec6..26ed1cb6d8 100644 --- a/features/invite/impl/src/main/res/values-tr/translations.xml +++ b/features/invite/impl/src/main/res/values-tr/translations.xml @@ -10,5 +10,4 @@ "Evet, reddet ve engelle" "Bu odaya katılma davetini reddetmek istediğinizden emin misiniz? Bu aynı zamanda %1$s sizinle iletişim kurmasını veya sizi odalara davet etmesini de engeller." "Daveti reddet ve engelle" - "Reddet ve engelle" diff --git a/features/invite/impl/src/main/res/values-uk/translations.xml b/features/invite/impl/src/main/res/values-uk/translations.xml index 9a5aef7298..8752f5707e 100644 --- a/features/invite/impl/src/main/res/values-uk/translations.xml +++ b/features/invite/impl/src/main/res/values-uk/translations.xml @@ -3,7 +3,7 @@ "Ви не бачитимете повідомлень або запрошень у кімнату від цього користувача" "Заблокувати користувача" "Поскаржитися на цю кімнату постачальнику облікового запису." - "Опишіть причину скарги…" + "Опишіть причину…" "Відхилити та заблокувати" "Ви впевнені, що хочете відхилити запрошення приєднатися до %1$s?" "Відхилити запрошення" diff --git a/features/invite/impl/src/main/res/values-uz/translations.xml b/features/invite/impl/src/main/res/values-uz/translations.xml index bf1f398712..1db05ad0d9 100644 --- a/features/invite/impl/src/main/res/values-uz/translations.xml +++ b/features/invite/impl/src/main/res/values-uz/translations.xml @@ -7,4 +7,5 @@ "Chatni rad etish" "Takliflar yo\'q" "%1$s(%2$s ) sizni taklif qildi" + "Rad etish va bloklash" 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 2ce5cd929a..5e2b00a3f1 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 @@ -18,8 +18,8 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.ContributesBinding -import dev.zacsweers.metro.Inject import io.element.android.features.invitepeople.api.InvitePeopleEvents import io.element.android.features.invitepeople.api.InvitePeoplePresenter import io.element.android.features.invitepeople.api.InvitePeopleState @@ -51,7 +51,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -@Inject +@AssistedInject class DefaultInvitePeoplePresenter( @Assisted private val joinedRoom: JoinedRoom?, @Assisted private val roomId: RoomId, diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomFlowNode.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomFlowNode.kt index f827df0e55..f501752544 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomFlowNode.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomFlowNode.kt @@ -17,7 +17,7 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.invite.api.InviteData import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteView @@ -30,7 +30,7 @@ import io.element.android.libraries.di.SessionScope import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class JoinRoomFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 cb7ad57837..6e123630d6 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 @@ -21,7 +21,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +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 @@ -53,13 +53,13 @@ import io.element.android.libraries.matrix.api.spaces.SpaceRoom import io.element.android.libraries.matrix.ui.model.toInviteSender import io.element.android.libraries.matrix.ui.safety.rememberHideInvitesAvatar import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import java.util.Optional import kotlin.jvm.optionals.getOrNull -@Inject +@AssistedInject class JoinRoomPresenter( @Assisted private val roomId: RoomId, @Assisted private val roomIdOrAlias: RoomIdOrAlias, @@ -94,9 +94,7 @@ class JoinRoomPresenter( val roomInfo by remember { matrixClient.getRoomInfoFlow(roomId) }.collectAsState(initial = Optional.empty()) - val spaceRoom by remember { - spaceList.currentSpaceFlow() - }.collectAsState() + val spaceRoom by spaceList.currentSpaceFlow.collectAsState() val joinAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val knockAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val cancelKnockAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } @@ -279,7 +277,7 @@ private fun RoomPreviewInfo.toContentState(membershipDetails: RoomMembershipDeta private fun SpaceRoom.toContentState(): ContentState { return ContentState.Loaded( roomId = roomId, - name = name, + name = displayName, topic = topic, alias = canonicalAlias, numberOfMembers = numJoinedMembers.toLong(), @@ -293,7 +291,7 @@ private fun SpaceRoom.toContentState(): ContentState { joinRule = joinRule, details = LoadedDetails.Space( childrenCount = childrenCount, - heroes = heroes.toPersistentList(), + heroes = heroes.toImmutableList(), ) ) } diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt index 8eeecab83c..e5f7df67a3 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt @@ -9,8 +9,8 @@ package io.element.android.features.joinroom.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider 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.AcceptDeclineInviteState +import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInviteState import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize @@ -24,7 +24,7 @@ import io.element.android.libraries.matrix.api.room.join.JoinRoom import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.model.InviteSender -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList open class JoinRoomStateProvider : PreviewParameterProvider { override val values: Sequence @@ -189,7 +189,7 @@ fun aLoadedDetailsSpace( heroes: List = emptyList(), ) = LoadedDetails.Space( childrenCount = childrenCount, - heroes = heroes.toPersistentList() + heroes = heroes.toImmutableList() ) fun aJoinRoomState( @@ -219,16 +219,6 @@ fun aJoinRoomState( eventSink = eventSink ) -internal fun anAcceptDeclineInviteState( - acceptAction: AsyncAction = AsyncAction.Uninitialized, - declineAction: AsyncAction = AsyncAction.Uninitialized, - eventSink: (AcceptDeclineInviteEvents) -> Unit = {} -) = AcceptDeclineInviteState( - acceptAction = acceptAction, - declineAction = declineAction, - eventSink = eventSink, -) - internal fun anInviteSender( userId: UserId = UserId("@bob:domain"), displayName: String = "Bob", 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 6da7aadfe7..38d084c7d3 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 @@ -74,7 +74,7 @@ import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.placeholderBackground import io.element.android.libraries.matrix.api.core.RoomIdOrAlias -import io.element.android.libraries.matrix.api.room.join.JoinRule +import io.element.android.libraries.matrix.api.spaces.SpaceRoomVisibility import io.element.android.libraries.matrix.ui.components.SpaceInfoRow import io.element.android.libraries.matrix.ui.components.SpaceMembersView import io.element.android.libraries.matrix.ui.model.InviteSender @@ -567,10 +567,7 @@ private fun DefaultLoadedContent( subtitle = { when { contentState.details is LoadedDetails.Space -> { - SpaceInfoRow( - joinRule = contentState.joinRule ?: JoinRule.Public, - numberOfRooms = contentState.details.childrenCount, - ) + SpaceInfoRow(visibility = SpaceRoomVisibility.fromJoinRule(contentState.joinRule)) } contentState.alias != null -> { RoomPreviewSubtitleAtom(contentState.alias.value) diff --git a/features/joinroom/impl/src/main/res/values-be/translations.xml b/features/joinroom/impl/src/main/res/values-be/translations.xml index 056a4394f7..1986a761b0 100644 --- a/features/joinroom/impl/src/main/res/values-be/translations.xml +++ b/features/joinroom/impl/src/main/res/values-be/translations.xml @@ -1,6 +1,6 @@ - "Далучыцца да пакоя" + "Далучыцца" "Націсніце, каб далучыцца" "%1$s пакуль не падтрымлівае прасторы. Вы можаце атрымаць доступ да прастор праз вэб-старонку." "Прасторы пакуль не падтрымліваюцца" diff --git a/features/joinroom/impl/src/main/res/values-bg/translations.xml b/features/joinroom/impl/src/main/res/values-bg/translations.xml index e9f7c38fcf..b395310bdd 100644 --- a/features/joinroom/impl/src/main/res/values-bg/translations.xml +++ b/features/joinroom/impl/src/main/res/values-bg/translations.xml @@ -1,4 +1,5 @@ - "Присъединяване към стаята" + "Отхвърляне и блокиране" + "Присъединяване" diff --git a/features/joinroom/impl/src/main/res/values-cs/translations.xml b/features/joinroom/impl/src/main/res/values-cs/translations.xml index 2e0720ea42..0c00c0c4bd 100644 --- a/features/joinroom/impl/src/main/res/values-cs/translations.xml +++ b/features/joinroom/impl/src/main/res/values-cs/translations.xml @@ -15,7 +15,8 @@ "Tato místnost je buď určena pouze pro zvané, nebo do ní může být omezen přístup na úrovni prostoru." "Zapomenout na tuto místnost" "Abyste se mohli připojit k této místnosti, potřebujete pozvánku." - "Připojit se do místnosti" + "Pozván(a)" + "Vstoupit" "Abyste se mohli připojit, musíte být pozváni nebo být členem některého prostoru." "Zaklepejte a připojte se" "Povolené znaky %1$d z %2$d" diff --git a/features/joinroom/impl/src/main/res/values-cy/translations.xml b/features/joinroom/impl/src/main/res/values-cy/translations.xml index 480ccdbb6c..8b5e3c2d6a 100644 --- a/features/joinroom/impl/src/main/res/values-cy/translations.xml +++ b/features/joinroom/impl/src/main/res/values-cy/translations.xml @@ -15,7 +15,8 @@ "Mae\'r ystafell hon naill ai drwy wahoddiad yn unig neu efallai y bydd cyfyngiadau ar fynediad ar lefel y gofod." "Anghofiwch yr ystafell hon" "Mae angen gwahoddiad arnoch chi er mwyn ymuno â\'r ystafell hon" - "Ymuno â\'r ystafell" + "Gwahoddwyd gan" + "Ymuno" "Efallai y bydd angen i chi gael eich gwahodd neu fod yn aelod o ofod er mwyn ymuno." "Anfon cais i ymuno" "Nodau a ganiateir %1$d o %2$d" diff --git a/features/joinroom/impl/src/main/res/values-da/translations.xml b/features/joinroom/impl/src/main/res/values-da/translations.xml index ac260472e0..ac1cd26191 100644 --- a/features/joinroom/impl/src/main/res/values-da/translations.xml +++ b/features/joinroom/impl/src/main/res/values-da/translations.xml @@ -1,7 +1,7 @@ - "Du blev spærret fra dette rum af %1$s." - "Du blev spærret fra dette rum" + "Du blev spærret af %1$s." + "Du blev spærret" "Årsag: %1$s." "Annuller anmodning" "Ja, annullér" @@ -11,11 +11,12 @@ "Er du sikker på, at du vil afvise invitationen til at deltage i dette rum? Dette forhindrer også %1$s i at kontakte dig eller invitere dig til andre rum." "Afvis invitation og blokér" "Afvis og blokér" - "Deltagelse i rummet fejlede." - "Dette rum er enten kun for gæster, eller der kan være sat begrænsninger for adgangen på klyngeniveau." - "Glem dette rum" - "Du har brug for en invitation for at deltage i dette rum" - "Deltag i rummet" + "Deltagelse fejlede." + "Du skal enten inviteres til at deltage, eller der kan være adgangsbegrænsninger." + "Glem" + "Du har brug for en invitation for at deltage" + "Inviteret af" + "Deltag" "Du skal muligvis være inviteret eller være medlem af en klynge for at deltage." "Send anmodning om at deltage" "Tilladte tegn %1$d af %2$d" diff --git a/features/joinroom/impl/src/main/res/values-de/translations.xml b/features/joinroom/impl/src/main/res/values-de/translations.xml index 42c2b3fce4..c6d97d6fcb 100644 --- a/features/joinroom/impl/src/main/res/values-de/translations.xml +++ b/features/joinroom/impl/src/main/res/values-de/translations.xml @@ -1,7 +1,7 @@ - "Du wurdest von %1$s für diesen Chat gesperrt." - "Du wurdest für diesen Chat gesperrt" + "Du wurdest von %1$s gesperrt." + "Du wurdest gesperrt" "Grund:%1$s." "Anfrage abbrechen" "Ja, abbrechen" @@ -11,11 +11,12 @@ "Bist du sicher, dass du die Einladung zu diesem Chat ablehnen möchtest? Dadurch wird auch jede weitere Kontaktaufnahme oder Chat Einladung von %1$s blockiert." "Einladung ablehnen & Nutzer blockieren" "Ablehnen und blockieren" - "Der Beitritt zum Chat schlug fehl." - "Dieser Chat ist entweder nur auf Einladung zugänglich oder es gibt andere Zugangsbeschränkungen durch Spaces." - "Vergiss diesen Chat" - "Du benötigst eine Einladung, um diesem Chat beizutreten" - "Chat beitreten" + "Beitritt fehlgeschlagen" + "Du musst entweder eingeladen werden, um beizutreten, oder es gibt möglicherweise Zugriffsbeschränkungen." + "Vergessen" + "Du benötigst eine Einladung, um beizutreten" + "Eingeladen von" + "Beitreten" "Möglicherweise musst du eingeladen werden oder ein Mitglied eines Spaces sein, um beitreten zu können." "Anklopfen" "%1$d von %2$d erlaubte Zeichen" diff --git a/features/joinroom/impl/src/main/res/values-el/translations.xml b/features/joinroom/impl/src/main/res/values-el/translations.xml index 9a7296b93b..b12bbb9374 100644 --- a/features/joinroom/impl/src/main/res/values-el/translations.xml +++ b/features/joinroom/impl/src/main/res/values-el/translations.xml @@ -15,7 +15,7 @@ "Αυτή η αίθουσα είναι είτε μόνο για προσκεκλημένους είτε ενδέχεται να υπάρχουν περιορισμοί πρόσβασης σε επίπεδο χώρου." "Ξεχάστε αυτή την αίθουσα" "Χρειάζεστε πρόσκληση για να συμμετάσχετε σε αυτή την αίθουσα" - "Συμμετοχή στην αίθουσα" + "Συμμετοχή" "Ενδέχεται να χρειαστεί να προσκληθείτε ή να είστε μέλος ενός χώρου για να συμμετάσχετε." "Χτύπα για συμμετοχή" "Μήνυμα (προαιρετικό)" diff --git a/features/joinroom/impl/src/main/res/values-es/translations.xml b/features/joinroom/impl/src/main/res/values-es/translations.xml index b98536b1be..1f905bbbf7 100644 --- a/features/joinroom/impl/src/main/res/values-es/translations.xml +++ b/features/joinroom/impl/src/main/res/values-es/translations.xml @@ -15,7 +15,7 @@ "O bien solo se puede acceder a esta sala con invitación, o puede que haya restricciones de acceso a nivel de espacio." "Olvidar esta sala" "Necesitas una invitación para unirte a esta sala" - "Unirse a la sala" + "Unirse" "Es posible que necesites ser invitado o ser miembro de un espacio para poder unirte." "Enviar solicitud de unión" "Mensaje (opcional)" diff --git a/features/joinroom/impl/src/main/res/values-et/translations.xml b/features/joinroom/impl/src/main/res/values-et/translations.xml index b69ca16d26..b3c51abbf1 100644 --- a/features/joinroom/impl/src/main/res/values-et/translations.xml +++ b/features/joinroom/impl/src/main/res/values-et/translations.xml @@ -15,7 +15,8 @@ "Ligipääs siia jututuppa on võimalik vaid kutse alusel või kehtivad siin kogukonnakohased piirangud." "Unusta see jututuba" "Selle jututoaga liitumiseks vajad sa kutset" - "Liitu jututoaga" + "Kutsuja" + "Liitu" "Selle jututoaga liitumiseks sa vajad kutset või pead juba olema kogukonna liige." "Liitumiseks koputa jututoa uksele" "Lubatud tähemärke: %1$d / %2$d" diff --git a/features/joinroom/impl/src/main/res/values-eu/translations.xml b/features/joinroom/impl/src/main/res/values-eu/translations.xml index e22c65fcb9..2fe6a76104 100644 --- a/features/joinroom/impl/src/main/res/values-eu/translations.xml +++ b/features/joinroom/impl/src/main/res/values-eu/translations.xml @@ -4,10 +4,10 @@ "Utzi eskaera bertan behera" "Bai, utzi bertan behera" "Eman gonbidapenari ezetza eta blokeatu" - "Eman ezetza eta blokeatu" + "Baztertu eta blokeatu" "Gelara sartzeak huts egin du." "Ahaztu gela hau" - "Sartu gelan" + "Elkartu" "Bidali batzeko eskaera" "Mezua (aukerakoa)" "Sartzeko eskaera bidali da" diff --git a/features/joinroom/impl/src/main/res/values-fa/translations.xml b/features/joinroom/impl/src/main/res/values-fa/translations.xml index 529177cedb..77468f41ef 100644 --- a/features/joinroom/impl/src/main/res/values-fa/translations.xml +++ b/features/joinroom/impl/src/main/res/values-fa/translations.xml @@ -12,7 +12,7 @@ "پیوستن به اتاق شکست خورد." "فراموشی این اتاق" "برای پیوستن به این اتاق نیاز به دعوت دارید" - "پیوستن به اتاق" + "پیوستن" "در زدن برای پیوستن" "پیام (اختیاری)" "درخواست پیوستن فرستاده شد" diff --git a/features/joinroom/impl/src/main/res/values-fi/translations.xml b/features/joinroom/impl/src/main/res/values-fi/translations.xml index b9d2da01ad..9a8a7e2a16 100644 --- a/features/joinroom/impl/src/main/res/values-fi/translations.xml +++ b/features/joinroom/impl/src/main/res/values-fi/translations.xml @@ -1,7 +1,7 @@ - "%1$s antoi sinulle porttikiellon tästä huoneesta." - "Sinulle on annettu porttikielto tästä huoneesta" + "%1$s antoi sinulle porttikiellon." + "Sinulle on annettu porttikielto" "Syy: %1$s." "Peruuta pyyntö" "Kyllä, peruuta" @@ -11,11 +11,11 @@ "Oletko varma, että haluat kieltäytyä kutsusta liittyä tähän huoneeseen? Tämä estää myös käyttäjää %1$s ottamasta sinuun yhteyttä tai kutsumasta sinua huoneisiin." "Hylkää kutsu ja estä" "Hylkää ja estä" - "Huoneeseen liittyminen epäonnistui." - "Tämä huone on tarkoitettu vain kutsutuille, tai siihen saattaa liittyä rajoituksia tilatasolla." - "Unohda tämä huone" - "Tarvitset kutsun liittyäksesi tähän huoneeseen" - "Liity huoneeseen" + "Liittyminen epäonnistui" + "Sinun on joko saatava kutsu liittyäksesi tai pääsyyn voi olla rajoituksia." + "Unohda" + "Tarvitset kutsun liittyäksesi" + "Liity" "Saatat tarvita kutsun tai olla tilan jäsen, jotta voit liittyä." "Lähetä liittymispyyntö" "%1$d merkkiä käytetty, %2$d merkkiä sallittu" diff --git a/features/joinroom/impl/src/main/res/values-fr/translations.xml b/features/joinroom/impl/src/main/res/values-fr/translations.xml index 5c200b62dd..7ad0317c35 100644 --- a/features/joinroom/impl/src/main/res/values-fr/translations.xml +++ b/features/joinroom/impl/src/main/res/values-fr/translations.xml @@ -1,7 +1,7 @@ - "Vous avez été banni de ce salon par %1$s." - "Vous avez été banni de ce salon" + "Vous avez été banni(e) par %1$s." + "Vous avez été banni(e)" "Motif: %1$s." "Annuler la demande" "Oui, annuler" @@ -11,10 +11,11 @@ "Êtes-vous sûr de vouloir refuser l’invitation à rejoindre ce salon ? Cela empêchera également %1$s de vous contacter ou de vous inviter dans les salons." "Refuser l’invitation et bloquer" "Refuser et bloquer" - "Rejoindre le salon a échoué." - "Ce salon est accessible uniquement sur invitation ou il peut y avoir des restrictions d’accès au niveau de l’espace." - "Oublier ce salon" - "Vous avez besoin d’une invitation pour rejoindre ce salon" + "L’opération a échoué." + "Soit vous devez être invité(e) pour rejoindre, soit il peut y avoir des restrictions d’accès." + "Oublier" + "Vous avez besoin d’une invitation pour pouvoir rejoindre" + "Invité(e) par" "Rejoindre" "Il est possible que vous deviez être invité ou être membre d’un Espace pour pouvoir rejoindre le salon." "Demander à joindre" diff --git a/features/joinroom/impl/src/main/res/values-hu/translations.xml b/features/joinroom/impl/src/main/res/values-hu/translations.xml index 6a3f9b836f..8fafb9aa7a 100644 --- a/features/joinroom/impl/src/main/res/values-hu/translations.xml +++ b/features/joinroom/impl/src/main/res/values-hu/translations.xml @@ -10,12 +10,13 @@ "Igen, elutasítás és blokkolás" "Biztos, hogy elutasítja a meghívást, hogy csatlakozzon ehhez a szobához? Ez azt is megakadályozza, hogy %1$s kapcsolatba lépjen Önnel, vagy szobákba hívja." "Meghívó elutasítása és blokkolás" - "Elutasítás és blokkolás" + "Elutasítás és letiltás" "A szobához való csatlakozás sikertelen." "Ebbe a szobába csak meghívóval vagy tértagsággal lehet belépni." "Szoba elfelejtése" "Meghívóra van szüksége ahhoz, hogy csatlakozzon ehhez a szobához" - "Csatlakozás a szobához" + "Meghívta:" + "Csatlakozás" "A csatlakozáshoz meghívásra vagy tértagságra lehet szüksége." "Kopogtasson a csatlakozáshoz" "Engedélyezett karakterek: %1$d / %2$d" diff --git a/features/joinroom/impl/src/main/res/values-in/translations.xml b/features/joinroom/impl/src/main/res/values-in/translations.xml index 16c078c6ff..59e3aba259 100644 --- a/features/joinroom/impl/src/main/res/values-in/translations.xml +++ b/features/joinroom/impl/src/main/res/values-in/translations.xml @@ -15,7 +15,7 @@ "Ruangan ini hanya untuk undangan atau mungkin ada pembatasan akses pada tingkat space." "Lupakan ruangan ini" "Anda memerlukan undangan untuk bergabung dalam ruangan ini" - "Gabung dengan ruangan" + "Gabung" "Anda mungkin perlu diundang atau menjadi anggota space untuk bergabung." "Ketuk untuk bergabung" "Pesan (opsional)" diff --git a/features/joinroom/impl/src/main/res/values-it/translations.xml b/features/joinroom/impl/src/main/res/values-it/translations.xml index 1ea811d8dc..deb025db27 100644 --- a/features/joinroom/impl/src/main/res/values-it/translations.xml +++ b/features/joinroom/impl/src/main/res/values-it/translations.xml @@ -15,7 +15,7 @@ "Questa stanza è solo su invito o potrebbero esserci delle restrizioni all\'accesso al livello dello spazio." "Dimentica questa stanza" "Hai bisogno di un invito per entrare in questa stanza" - "Entra nella stanza" + "Entra" "Potrebbe essere necessario essere invitati o essere membro di uno spazio per partecipare." "Bussa per partecipare" "Caratteri consentiti: %1$d di %2$d" diff --git a/features/joinroom/impl/src/main/res/values-ka/translations.xml b/features/joinroom/impl/src/main/res/values-ka/translations.xml new file mode 100644 index 0000000000..038b80722b --- /dev/null +++ b/features/joinroom/impl/src/main/res/values-ka/translations.xml @@ -0,0 +1,4 @@ + + + "გაწევრიანება" + diff --git a/features/joinroom/impl/src/main/res/values-ko/translations.xml b/features/joinroom/impl/src/main/res/values-ko/translations.xml index b6edc8447e..80225ac402 100644 --- a/features/joinroom/impl/src/main/res/values-ko/translations.xml +++ b/features/joinroom/impl/src/main/res/values-ko/translations.xml @@ -15,7 +15,7 @@ "이 방은 초대 전용이거나 스페이스 수준에서 액세스 제한이 있을 수 있습니다." "이 방 지우기" "이 방에 참여하려면 초대장이 필요합니다." - "방에 참여하기" + "참가하기" "참여하려면 초대 또는 스페이스의 회원이어야 할 수 있습니다." "가입 요청 보내기" "%2$d의 %1$d 문자가 허용됨" diff --git a/features/joinroom/impl/src/main/res/values-nb/translations.xml b/features/joinroom/impl/src/main/res/values-nb/translations.xml index a349def982..5c4873bc57 100644 --- a/features/joinroom/impl/src/main/res/values-nb/translations.xml +++ b/features/joinroom/impl/src/main/res/values-nb/translations.xml @@ -1,7 +1,7 @@ - "Du ble utestengt fra dette rommet av %1$s." - "Du ble utestengt fra dette rommet" + "Du ble utestengt av %1$s." + "Du ble utestengt" "Årsak: %1$s." "Avbryt forespørsel" "Ja, avbryt" @@ -11,11 +11,12 @@ "Er du sikker på at du vil avslå invitasjonen til å bli med i dette rommet? Dette vil også forhindre %1$s fra å kontakte deg eller invitere deg til rom." "Avslå invitasjon og blokker" "Avslå og blokker" - "Å bli med i rommet mislyktes." - "Dette rommet er enten kun for inviterte, eller det kan være begrensninger for tilgang på områdenivå." - "Glem dette rommet" - "Du trenger en invitasjon for å bli med i dette rommet" - "Bli med i rommet" + "Kunne ikke bli med" + "Du må enten bli invitert til å bli med, eller så kan det være begrensninger på tilgangen." + "Glem" + "Du trenger en invitasjon for å bli med" + "Invitert av" + "Bli med" "Du må kanskje bli invitert eller være medlem av et område for å bli med." "Send forespørsel om å bli med" "Tillatte tegn %1$d av %2$d" diff --git a/features/joinroom/impl/src/main/res/values-nl/translations.xml b/features/joinroom/impl/src/main/res/values-nl/translations.xml index 3132629fc0..36d480b260 100644 --- a/features/joinroom/impl/src/main/res/values-nl/translations.xml +++ b/features/joinroom/impl/src/main/res/values-nl/translations.xml @@ -2,8 +2,9 @@ "Reden: %1$s." "Verzoek annuleren" + "Weigeren en blokkeren" "Deze kamer vergeten" - "Toetreden tot de kamer" + "Deelnemen" "Klop om deel te nemen" "Bericht (optioneel)" "Je ontvangt een uitnodiging om deel te nemen aan de kamer als je aanvraag wordt geaccepteerd." diff --git a/features/joinroom/impl/src/main/res/values-pl/translations.xml b/features/joinroom/impl/src/main/res/values-pl/translations.xml index 21042e2903..dea02ea7cd 100644 --- a/features/joinroom/impl/src/main/res/values-pl/translations.xml +++ b/features/joinroom/impl/src/main/res/values-pl/translations.xml @@ -15,7 +15,7 @@ "Ten pokój wymaga zaproszenia lub jest ograniczony z poziomu przestrzeni." "Zapomnij o tym pokoju" "Potrzebujesz zaproszenia, aby dołączyć do tego pokoju" - "Dołącz do pokoju" + "Dołącz" "Aby dołączyć, musisz uzyskać zaproszenie lub być członkiem danej przestrzeni." "Wyślij prośbę o dołączenie" "Dozwolone znaki %1$d z %2$d" diff --git a/features/joinroom/impl/src/main/res/values-pt-rBR/translations.xml b/features/joinroom/impl/src/main/res/values-pt-rBR/translations.xml index 38f91512d7..ed553bd93a 100644 --- a/features/joinroom/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/joinroom/impl/src/main/res/values-pt-rBR/translations.xml @@ -15,7 +15,7 @@ "Esta sala é apenas para convidados ou pode haver restrições de acesso a nível do espaço." "Esquecer esta sala" "Você precisa de um convite para entrar nesta sala" - "Entrar na sala" + "Entrar" "Talvez você precise ser convidado ou ser membro de um espaço para participar." "Enviar solicitação para entrar" "%1$d de %2$d caráteres permitidos" diff --git a/features/joinroom/impl/src/main/res/values-pt/translations.xml b/features/joinroom/impl/src/main/res/values-pt/translations.xml index 5bac83b9d2..4ed5e4219f 100644 --- a/features/joinroom/impl/src/main/res/values-pt/translations.xml +++ b/features/joinroom/impl/src/main/res/values-pt/translations.xml @@ -15,7 +15,8 @@ "A entrada nesta sala ou está limitada a convites ou a alguma configuração de espaço." "Esquecer esta sala" "Precisas de um convite para entrares nesta sala" - "Entrar na sala" + "Convidado por" + "Entrar" "Podes ter que ser convidado ou pertenceres a um espaço para poderes entrar." "Bater à porta" "%1$d de %2$d caracteres permitidos" diff --git a/features/joinroom/impl/src/main/res/values-ro/translations.xml b/features/joinroom/impl/src/main/res/values-ro/translations.xml index ca7764aa62..8326ce06bf 100644 --- a/features/joinroom/impl/src/main/res/values-ro/translations.xml +++ b/features/joinroom/impl/src/main/res/values-ro/translations.xml @@ -1,6 +1,6 @@ - "Ai fost exclus din această cameră de către %1$s." + "Ați fost exclus din această cameră de către %1$s." "Ați fost exclus din această cameră." "Motiv: %1$s." "Anulați cererea" @@ -15,7 +15,8 @@ "Această cameră este fie accesibilă numai pe bază de invitație, fie exista restricții de acces la nivel de spațiu." "Uitați această cameră" "Aveți nevoie de o invitație pentru a vă alătura acestei camere." - "Alăturați-vă camerei" + "Invitat de" + "Alăturați-vă" "Este posibil să fie necesar să fiți invitat sau să fiți membru al unui spațiu pentru a vă alătura." "Trimiteți o cerere de alăturare" "Caractere permise %1$d din %2$d" diff --git a/features/joinroom/impl/src/main/res/values-ru/translations.xml b/features/joinroom/impl/src/main/res/values-ru/translations.xml index 4c87355000..9b50f35da4 100644 --- a/features/joinroom/impl/src/main/res/values-ru/translations.xml +++ b/features/joinroom/impl/src/main/res/values-ru/translations.xml @@ -15,9 +15,11 @@ "Доступ в эту комнату возможен только по приглашениям или может быть ограничен на уровне пространства." "Забыть эту комнату" "Вам необходимо приглашение для того, чтобы присоединиться к этой комнате" - "Присоединиться к комнате" + "Приглашен" + "Присоединиться" "Чтобы присоединиться, вам необходимо приглашение или быть участником сообщества." "Отправить запрос на присоединение" + "Разрешенные символы %1$d %2$d" "Сообщение (опционально)" "Вы получите приглашение присоединиться к комнате, как только ваш запрос будет принят." "Запрос на присоединение отправлен" diff --git a/features/joinroom/impl/src/main/res/values-sk/translations.xml b/features/joinroom/impl/src/main/res/values-sk/translations.xml index 5ee2899674..35cc31af32 100644 --- a/features/joinroom/impl/src/main/res/values-sk/translations.xml +++ b/features/joinroom/impl/src/main/res/values-sk/translations.xml @@ -15,7 +15,7 @@ "Táto miestnosť je buď len pre pozvaných, alebo môžu existovať obmedzenia na prístup na úrovni priestoru." "Zabudnúť túto miestnosť" "Potrebujete pozvanie, aby ste sa mohli pripojiť k tejto miestnosti" - "Pripojiť sa do miestnosti" + "Pripojiť sa" "Možno budete musieť byť pozvaní alebo byť členom priestoru, aby ste sa mohli pripojiť." "Zaklopaním sa pripojíte" "Povolené znaky %1$d z %2$d" diff --git a/features/joinroom/impl/src/main/res/values-sv/translations.xml b/features/joinroom/impl/src/main/res/values-sv/translations.xml index 78924f1cd6..11af0da734 100644 --- a/features/joinroom/impl/src/main/res/values-sv/translations.xml +++ b/features/joinroom/impl/src/main/res/values-sv/translations.xml @@ -15,7 +15,7 @@ "Detta rum är antingen endast för inbjudna eller så kan det finnas begränsningar för åtkomst på utrymmesnivå." "Glöm det här rummet" "Du behöver en inbjudan för att gå med i detta rum" - "Gå med i rummet" + "Gå med" "Du kan behöva bli inbjuden eller vara medlem i ett utrymme för att gå med." "Knacka för att gå med" "Tillåtna tecken %1$d av %2$d" diff --git a/features/joinroom/impl/src/main/res/values-tr/translations.xml b/features/joinroom/impl/src/main/res/values-tr/translations.xml index fb3edc84b2..abd08d3c86 100644 --- a/features/joinroom/impl/src/main/res/values-tr/translations.xml +++ b/features/joinroom/impl/src/main/res/values-tr/translations.xml @@ -10,12 +10,11 @@ "Evet, reddet ve engelle" "Bu odaya katılma davetini reddetmek istediğinizden emin misiniz? Bu aynı zamanda %1$s sizinle iletişim kurmasını veya sizi odalara davet etmesini de engeller." "Daveti reddet ve engelle" - "Reddet ve engelle" "Odaya katılım başarısız oldu." "Bu odaya yalnızca davetle girilebilir veya alan düzeyinde erişim kısıtlamaları olabilir." "Bu odayı unut" "Bu odaya katılmak için bir davete ihtiyacınız var" - "Odaya katıl" + "Katıl" "Katılmak için davet edilmeniz veya bir alana üye olmanız gerekebilir." "Katılma isteği gönder" "Mesaj (isteğe bağlı)" diff --git a/features/joinroom/impl/src/main/res/values-uk/translations.xml b/features/joinroom/impl/src/main/res/values-uk/translations.xml index 6bdd39807b..90d5b01441 100644 --- a/features/joinroom/impl/src/main/res/values-uk/translations.xml +++ b/features/joinroom/impl/src/main/res/values-uk/translations.xml @@ -15,7 +15,7 @@ "Ця кімната доступна лише за запрошенням або на рівні простору можуть бути обмеження доступу." "Забути цю кімнату" "Вам потрібне запрошення, щоб приєднатися до цієї кімнати" - "Приєднатися до кімнати" + "Доєднатися" "Можливо, вам знадобиться отримати запрошення або стати учасником простору, щоб приєднатися." "Постукати, щоб приєднатися" "Дозволені символи %1$d з %2$d" diff --git a/features/joinroom/impl/src/main/res/values-ur/translations.xml b/features/joinroom/impl/src/main/res/values-ur/translations.xml index af34c73710..4bb703073c 100644 --- a/features/joinroom/impl/src/main/res/values-ur/translations.xml +++ b/features/joinroom/impl/src/main/res/values-ur/translations.xml @@ -1,6 +1,6 @@ - "کمرے میں شامل ہوں" + "شامل ہوں" "شامل ہونے کی درخواست بھیجیں" "%1$s ابھی تک خالی جگہوں کی حمایت نہیں کرتا۔ آپ جال پر خالی جگہوں تک رسائی حاصل کرسکتے ہیں۔" "ابھی تک جگہیں تعاون یافتہ نہیں" diff --git a/features/joinroom/impl/src/main/res/values-uz/translations.xml b/features/joinroom/impl/src/main/res/values-uz/translations.xml index 2fbb5e38d7..e8c50922c4 100644 --- a/features/joinroom/impl/src/main/res/values-uz/translations.xml +++ b/features/joinroom/impl/src/main/res/values-uz/translations.xml @@ -4,7 +4,8 @@ "Ha, bekor qiling" "Bu xonaga qo‘shilish so‘rovingizni bekor qilishni xohlayotganingizga ishonchingiz komilmi?" "Qo‘shilish so‘rovini bekor qilish" - "Xonaga qoʻshilish" + "Rad etish va bloklash" + "Qo\'shilish" "Qoʻshilish soʻrovini yuborish" "Xabar (ixtiyoriy)" "Agar so‘rovingiz qabul qilinsa, xonaga qo‘shilish taklifini olasiz." diff --git a/features/joinroom/impl/src/main/res/values-zh-rTW/translations.xml b/features/joinroom/impl/src/main/res/values-zh-rTW/translations.xml index a66510111c..aa7bbc261d 100644 --- a/features/joinroom/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/joinroom/impl/src/main/res/values-zh-rTW/translations.xml @@ -15,7 +15,7 @@ "此聊天室僅有受邀者才能進入,或是在空間層級有存取限制。" "忘記此聊天室" "您需要獲得邀請才能加入此聊天室" - "加入聊天室" + "加入" "您可能需要被邀請成為空間的成員才能加入。" "傳送加入請求" "允許的字元 %1$d 中的 %2$d" diff --git a/features/joinroom/impl/src/main/res/values-zh/translations.xml b/features/joinroom/impl/src/main/res/values-zh/translations.xml index 05c52c9293..52361e01aa 100644 --- a/features/joinroom/impl/src/main/res/values-zh/translations.xml +++ b/features/joinroom/impl/src/main/res/values-zh/translations.xml @@ -15,7 +15,8 @@ "要么此房间仅限受邀者,要么可能在空间层级有加入限制。" "忘记这个房间" "你需要邀请才能加入这个房间" - "加入聊天室" + "受邀于" + "加入" "您可能需要受到邀请或成为某个空间的成员才能加入。" "加入聊天室" "允许的字符数量 %2$d中的%1$d" diff --git a/features/joinroom/impl/src/main/res/values/localazy.xml b/features/joinroom/impl/src/main/res/values/localazy.xml index 3d6a319aa6..0e1787fc0b 100644 --- a/features/joinroom/impl/src/main/res/values/localazy.xml +++ b/features/joinroom/impl/src/main/res/values/localazy.xml @@ -1,7 +1,7 @@ - "You were banned from this room by %1$s." - "You were banned from this room" + "You were banned by %1$s." + "You were banned" "Reason: %1$s." "Cancel request" "Yes, cancel" @@ -11,12 +11,12 @@ "Are you sure you want to decline the invite to join this room? This will also prevent %1$s from contacting you or inviting you to rooms." "Decline invite & block" "Decline and block" - "Joining the room failed." - "This room is either invite-only or there might be restrictions to access at space level." - "Forget this room" - "You need an invite in order to join this room" + "Joining failed" + "You either need to be invited to join or there might be restrictions to access." + "Forget" + "You need an invite in order to join" "Invited by" - "Join room" + "Join" "You may need to be invited or be a member of a space in order to join." "Send request to join" "Allowed characters %1$d of %2$d" 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 574c310d1f..10b5c1a712 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 @@ -13,6 +13,7 @@ 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.AcceptDeclineInviteState +import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInviteState import io.element.android.features.invite.api.toInviteData import io.element.android.features.invite.test.InMemorySeenInvitesStore import io.element.android.features.joinroom.impl.di.CancelKnockRoom diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListNode.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListNode.kt index 2fa3542174..a9bec1556b 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListNode.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListNode.kt @@ -13,12 +13,12 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.RoomScope @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class KnockRequestsListNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/knockrequests/impl/src/main/res/values-da/translations.xml b/features/knockrequests/impl/src/main/res/values-da/translations.xml index 30ac6a8d30..795cda658c 100644 --- a/features/knockrequests/impl/src/main/res/values-da/translations.xml +++ b/features/knockrequests/impl/src/main/res/values-da/translations.xml @@ -32,5 +32,5 @@ "Se alle" "Accepter" "%1$s ønsker at deltage i dette rum" - "Se" + "Vis" diff --git a/features/knockrequests/impl/src/main/res/values-el/translations.xml b/features/knockrequests/impl/src/main/res/values-el/translations.xml index 2066b2e293..5c8bec16f8 100644 --- a/features/knockrequests/impl/src/main/res/values-el/translations.xml +++ b/features/knockrequests/impl/src/main/res/values-el/translations.xml @@ -32,5 +32,4 @@ "Προβολή όλων" "Αποδοχή" "%1$s θέλει να συμμετάσχει σε αυτή την αίθουσα" - "Προβολή" diff --git a/features/knockrequests/impl/src/main/res/values-es/translations.xml b/features/knockrequests/impl/src/main/res/values-es/translations.xml index ea9f74544d..8f9ab72b70 100644 --- a/features/knockrequests/impl/src/main/res/values-es/translations.xml +++ b/features/knockrequests/impl/src/main/res/values-es/translations.xml @@ -32,5 +32,4 @@ "Ver todo" "Aceptar" "%1$s quiere unirse a esta sala" - "Ver" diff --git a/features/knockrequests/impl/src/main/res/values-nl/translations.xml b/features/knockrequests/impl/src/main/res/values-nl/translations.xml index 46787d05e0..a78d6222ca 100644 --- a/features/knockrequests/impl/src/main/res/values-nl/translations.xml +++ b/features/knockrequests/impl/src/main/res/values-nl/translations.xml @@ -1,4 +1,5 @@ "Accepteren" + "Bekijken" diff --git a/features/knockrequests/impl/src/main/res/values-pt-rBR/translations.xml b/features/knockrequests/impl/src/main/res/values-pt-rBR/translations.xml index 4ccedac193..d6fcebb5ab 100644 --- a/features/knockrequests/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/knockrequests/impl/src/main/res/values-pt-rBR/translations.xml @@ -32,5 +32,5 @@ "Ver tudo" "Aceitar" "%1$s quer entrar nesta sala" - "Ver" + "Visualizar" diff --git a/features/knockrequests/impl/src/main/res/values-tr/translations.xml b/features/knockrequests/impl/src/main/res/values-tr/translations.xml index b6a070ee08..445d2aa2dd 100644 --- a/features/knockrequests/impl/src/main/res/values-tr/translations.xml +++ b/features/knockrequests/impl/src/main/res/values-tr/translations.xml @@ -32,5 +32,4 @@ "Tümünü görüntüle" "Kabul et" "%1$s bu odaya katılmak istiyor" - "Görüntüle" diff --git a/features/knockrequests/impl/src/main/res/values-uk/translations.xml b/features/knockrequests/impl/src/main/res/values-uk/translations.xml index 55c973fbf4..11a9b7fad4 100644 --- a/features/knockrequests/impl/src/main/res/values-uk/translations.xml +++ b/features/knockrequests/impl/src/main/res/values-uk/translations.xml @@ -33,5 +33,5 @@ "Переглянути все" "Прийняти" "%1$s хоче приєднатися до цієї кімнати" - "Перегляд" + "Переглянути" diff --git a/features/knockrequests/impl/src/main/res/values-uz/translations.xml b/features/knockrequests/impl/src/main/res/values-uz/translations.xml index d7e6705e67..8cad1166df 100644 --- a/features/knockrequests/impl/src/main/res/values-uz/translations.xml +++ b/features/knockrequests/impl/src/main/res/values-uz/translations.xml @@ -1,4 +1,5 @@ "Qabul qiling" + "Ko\'rish" diff --git a/features/leaveroom/api/src/main/res/values-bg/translations.xml b/features/leaveroom/api/src/main/res/values-bg/translations.xml index 69d203216a..8bb88631d5 100644 --- a/features/leaveroom/api/src/main/res/values-bg/translations.xml +++ b/features/leaveroom/api/src/main/res/values-bg/translations.xml @@ -1,4 +1,6 @@ + "Сигурни ли сте, че искате да напуснете тази стая? Вие сте единственият човек тук. Ако напуснете, никой няма да може да се присъедини в бъдеще, включително и вие." + "Сигурни ли сте, че искате да напуснете тази стая? Тази стая не е общодостъпна и няма да можете да се присъедините отново без покана." "Сигурни ли сте, че искате да напуснете стаята?" diff --git a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/DependenciesFlowNode.kt b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/DependenciesFlowNode.kt index 06305411ec..30e094e38e 100644 --- a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/DependenciesFlowNode.kt +++ b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/DependenciesFlowNode.kt @@ -17,7 +17,7 @@ import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.licenses.impl.details.DependenciesDetailsNode import io.element.android.features.licenses.impl.list.DependencyLicensesListNode @@ -28,7 +28,7 @@ import io.element.android.libraries.architecture.createNode import kotlinx.parcelize.Parcelize @ContributesNode(AppScope::class) -@Inject +@AssistedInject class DependenciesFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/details/DependenciesDetailsNode.kt b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/details/DependenciesDetailsNode.kt index 3fca4b13dd..4f55f8dfa8 100644 --- a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/details/DependenciesDetailsNode.kt +++ b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/details/DependenciesDetailsNode.kt @@ -14,14 +14,14 @@ 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.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.licenses.impl.model.DependencyLicenseItem import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs @ContributesNode(AppScope::class) -@Inject +@AssistedInject class DependenciesDetailsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListNode.kt b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListNode.kt index ca7caf7014..7280c2ad41 100644 --- a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListNode.kt +++ b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListNode.kt @@ -15,12 +15,12 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.licenses.impl.model.DependencyLicenseItem @ContributesNode(AppScope::class) -@Inject +@AssistedInject class DependencyLicensesListNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListPresenter.kt b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListPresenter.kt index 7c36a4f1ef..0af5d1cc47 100644 --- a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListPresenter.kt +++ b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListPresenter.kt @@ -20,7 +20,7 @@ import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.extensions.runCatchingExceptions import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList @Inject class DependencyLicensesListPresenter( @@ -37,7 +37,7 @@ class DependencyLicensesListPresenter( var filter by remember { mutableStateOf("") } LaunchedEffect(Unit) { runCatchingExceptions { - licenses = AsyncData.Success(licensesProvider.provides().toPersistentList()) + licenses = AsyncData.Success(licensesProvider.provides().toImmutableList()) }.onFailure { licenses = AsyncData.Failure(it) } @@ -50,7 +50,7 @@ class DependencyLicensesListPresenter( it.safeName.contains(safeFilter, ignoreCase = true) || it.groupId.contains(safeFilter, ignoreCase = true) || it.artifactId.contains(safeFilter, ignoreCase = true) - }.toPersistentList()) + }.toImmutableList()) } else { filteredLicenses = licenses } 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 06d7885be1..0ef520aadb 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 @@ -14,11 +14,11 @@ import com.google.accompanist.permissions.rememberMultiplePermissionsState import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.ContributesBinding -import dev.zacsweers.metro.Inject @Suppress("unused") -@Inject +@AssistedInject class DefaultPermissionsPresenter( @Assisted private val permissions: List ) : PermissionsPresenter { diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationNode.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationNode.kt index a0fc18cee8..8fff96e7b6 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationNode.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationNode.kt @@ -14,7 +14,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs @@ -24,7 +24,7 @@ import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.services.analytics.api.AnalyticsService @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class SendLocationNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt index f8d78b96e2..9914920ac3 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt @@ -17,7 +17,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.Composer import io.element.android.features.location.impl.common.MapDefaults import io.element.android.features.location.impl.common.actions.LocationActions @@ -36,7 +36,7 @@ import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.services.analytics.api.AnalyticsService import kotlinx.coroutines.launch -@Inject +@AssistedInject class SendLocationPresenter( permissionsPresenterFactory: PermissionsPresenter.Factory, private val room: JoinedRoom, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationNode.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationNode.kt index c0a6c5b6ad..d5977fa426 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationNode.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationNode.kt @@ -14,7 +14,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode import io.element.android.features.location.api.ShowLocationEntryPoint @@ -23,7 +23,7 @@ import io.element.android.libraries.di.RoomScope import io.element.android.services.analytics.api.AnalyticsService @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class ShowLocationNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 b304505367..7929843571 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 @@ -16,7 +16,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.features.location.api.Location import io.element.android.features.location.impl.common.MapDefaults import io.element.android.features.location.impl.common.actions.LocationActions @@ -26,7 +26,7 @@ import io.element.android.features.location.impl.common.permissions.PermissionsS import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.meta.BuildMeta -@Inject +@AssistedInject class ShowLocationPresenter( @Assisted private val location: Location, @Assisted private val description: String?, diff --git a/features/lockscreen/impl/build.gradle.kts b/features/lockscreen/impl/build.gradle.kts index 011d4919e7..998b763804 100644 --- a/features/lockscreen/impl/build.gradle.kts +++ b/features/lockscreen/impl/build.gradle.kts @@ -39,6 +39,7 @@ dependencies { implementation(projects.libraries.testtags) implementation(projects.libraries.uiUtils) implementation(projects.features.logout.api) + implementation(projects.libraries.uiCommon) implementation(projects.libraries.uiStrings) implementation(projects.libraries.sessionStorage.api) implementation(projects.services.appnavstate.api) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt index f572ef5cfb..94e2556ea2 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt @@ -16,7 +16,7 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.lockscreen.api.LockScreenEntryPoint import io.element.android.features.lockscreen.impl.settings.LockScreenSettingsFlowNode @@ -29,7 +29,7 @@ import io.element.android.libraries.di.SessionScope import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class LockScreenFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/model/PinEntry.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/model/PinEntry.kt index 78356fdddf..e9ee9a9903 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/model/PinEntry.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/model/PinEntry.kt @@ -8,7 +8,7 @@ package io.element.android.features.lockscreen.impl.pin.model import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList data class PinEntry( val digits: ImmutableList, @@ -17,7 +17,7 @@ data class PinEntry( fun createEmpty(size: Int): PinEntry { val digits = List(size) { PinDigit.Empty } return PinEntry( - digits = digits.toPersistentList() + digits = digits.toImmutableList() ) } } @@ -37,7 +37,7 @@ data class PinEntry( newDigits[index] = PinDigit.Filled(char) } } - return copy(digits = newDigits.toPersistentList()) + return copy(digits = newDigits.toImmutableList()) } fun deleteLast(): PinEntry { @@ -46,7 +46,7 @@ data class PinEntry( newDigits.indexOfLast { it is PinDigit.Filled }.also { lastFilled -> newDigits[lastFilled] = PinDigit.Empty } - return copy(digits = newDigits.toPersistentList()) + return copy(digits = newDigits.toImmutableList()) } fun addDigit(digit: Char): PinEntry { @@ -55,7 +55,7 @@ data class PinEntry( newDigits.indexOfFirst { it is PinDigit.Empty }.also { firstEmpty -> newDigits[firstEmpty] = PinDigit.Filled(digit) } - return copy(digits = newDigits.toPersistentList()) + return copy(digits = newDigits.toImmutableList()) } fun clear(): PinEntry { diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt index b32ca14c02..01aeaabe89 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt @@ -14,13 +14,12 @@ import androidx.lifecycle.lifecycleScope import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.node.node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.newRoot import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.lockscreen.impl.pin.DefaultPinCodeManagerCallback import io.element.android.features.lockscreen.impl.pin.PinCodeManager @@ -30,19 +29,20 @@ import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.ui.common.nodes.emptyNode import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class LockScreenSettingsFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, private val pinCodeManager: PinCodeManager, ) : BaseFlowNode( backstack = BackStack( - initialElement = NavTarget.Unknown, + initialElement = NavTarget.Loading, savedStateMap = buildContext.savedStateMap, ), buildContext = buildContext, @@ -50,7 +50,7 @@ class LockScreenSettingsFlowNode( ) { sealed interface NavTarget : Parcelable { @Parcelize - data object Unknown : NavTarget + data object Loading : NavTarget @Parcelize data object Unlock : NavTarget @@ -94,6 +94,9 @@ class LockScreenSettingsFlowNode( override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { + NavTarget.Loading -> { + emptyNode(buildContext) + } NavTarget.Unlock -> { val callback = object : PinUnlockNode.Callback { override fun onUnlock() { @@ -113,7 +116,6 @@ class LockScreenSettingsFlowNode( } createNode(buildContext, plugins = listOf(callback)) } - NavTarget.Unknown -> node(buildContext) { } } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsNode.kt index e821520509..5e27b815bc 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsNode.kt @@ -14,12 +14,12 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class LockScreenSettingsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt index e3b1704538..263cc8ea54 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt @@ -18,7 +18,7 @@ import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.newRoot import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.lockscreen.impl.biometric.BiometricAuthenticatorManager import io.element.android.features.lockscreen.impl.pin.DefaultPinCodeManagerCallback @@ -32,7 +32,7 @@ import io.element.android.libraries.di.SessionScope import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class LockScreenSetupFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricNode.kt index deebd9d10a..64da1a321c 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricNode.kt @@ -15,12 +15,12 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class SetupBiometricNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinNode.kt index 94cad2cf87..aceb955b88 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinNode.kt @@ -13,12 +13,12 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class SetupPinNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt index e5483f12a3..fba460f6ee 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt @@ -15,12 +15,12 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class PinUnlockNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/lockscreen/impl/src/main/res/values-bg/translations.xml b/features/lockscreen/impl/src/main/res/values-bg/translations.xml index af8e8a9853..7bd2895990 100644 --- a/features/lockscreen/impl/src/main/res/values-bg/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-bg/translations.xml @@ -1,7 +1,12 @@ + "биометрично удостоверяване" + "биометрично отключване" + "Отключване с биометрия" + "Потвърдете биометричните данни" "Забравихте PIN?" "Промяна на PIN кода" + "Разрешаване на биометрично отключване" "Премахване на PIN" "Сигурни ли сте, че искате да премахнете PIN?" "Премахване на PIN?" @@ -9,6 +14,10 @@ "Предпочитам да използвам PIN" "Избор на PIN" "Потвърждаване на PIN" + "Заключете %1$s, за да добавите допълнителна сигурност към вашите чатове. + +Изберете нещо запомнящо се. Ако забравите този PIN, ще бъдете излезли от приложението." + "Не можете да изберете това за ваш PIN код от съображения за сигурност" "Избор на различен PIN" "Моля, въведете един и същ PIN два пъти" "PINs не съвпадат" @@ -20,6 +29,7 @@ "Грешен PIN. Имате още %1$d шанс" "Грешен PIN. Имате още %1$d шанса" + "Използване на биометрия" "Използване на PIN" "Излизане…" diff --git a/features/login/impl/build.gradle.kts b/features/login/impl/build.gradle.kts index 314907c04a..690fe13578 100644 --- a/features/login/impl/build.gradle.kts +++ b/features/login/impl/build.gradle.kts @@ -40,6 +40,7 @@ dependencies { implementation(projects.libraries.testtags) implementation(projects.libraries.uiStrings) implementation(projects.libraries.permissions.api) + implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.qrcode) implementation(projects.libraries.oidc.api) implementation(projects.libraries.uiUtils) @@ -56,5 +57,6 @@ dependencies { testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.oidc.test) testImplementation(projects.libraries.permissions.test) + testImplementation(projects.libraries.sessionStorage.test) testImplementation(projects.libraries.wellknown.test) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt index a5522db870..4b83190f5d 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt @@ -24,7 +24,7 @@ import com.bumble.appyx.navmodel.backstack.operation.push import com.bumble.appyx.navmodel.backstack.operation.singleTop import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.compound.theme.ElementTheme import io.element.android.features.login.api.LoginEntryPoint @@ -51,7 +51,7 @@ import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize @ContributesNode(AppScope::class) -@Inject +@AssistedInject class LoginFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, @@ -87,7 +87,7 @@ class LoginFlowNode( // by pressing back or by closing the Custom Chrome Tab. lifecycleScope.launch { delay(5000) - oidcActionFlow.post(OidcAction.GoBack) + oidcActionFlow.post(OidcAction.GoBack(toUnblock = true)) } } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginHelper.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginHelper.kt index 70a0d97781..82ee87c372 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginHelper.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginHelper.kt @@ -94,9 +94,14 @@ class LoginHelper( } private suspend fun onOidcAction(oidcAction: OidcAction) { + if (oidcAction is OidcAction.GoBack && oidcAction.toUnblock && loginModeState.value !is AsyncData.Loading) { + // Ignore GoBack action if the current state is not Loading. This GoBack action is coming from LoginFlowNode. + // This can happen if there is an error, for instance attempt to login again on the same account. + return + } loginModeState.value = AsyncData.Loading() when (oidcAction) { - OidcAction.GoBack -> { + is OidcAction.GoBack -> { authenticationService.cancelOidcLogin() .onSuccess { loginModeState.value = AsyncData.Uninitialized diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt index 73127281bc..c3fe5eac47 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt @@ -14,7 +14,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.login.impl.R import io.element.android.features.login.impl.dialogs.SlidingSyncNotSupportedDialog import io.element.android.features.login.impl.error.ChangeServerError -import io.element.android.features.login.impl.error.ChangeServerErrorProvider import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported import io.element.android.libraries.androidutils.system.openGooglePlay import io.element.android.libraries.architecture.AsyncData @@ -23,6 +22,7 @@ import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.LocalBuildMeta +import io.element.android.libraries.matrix.api.auth.AuthenticationException import io.element.android.libraries.matrix.api.auth.OidcDetails import io.element.android.libraries.ui.strings.CommonStrings @@ -89,6 +89,12 @@ fun LoginModeView( onSubmit = onClearError, ) } + is AuthenticationException.AccountAlreadyLoggedIn -> { + ErrorDialog( + content = stringResource(CommonStrings.error_account_already_logged_in, error.message.orEmpty()), + onSubmit = onClearError, + ) + } else -> { ErrorDialog( content = stringResource(CommonStrings.error_unknown), @@ -113,7 +119,7 @@ fun LoginModeView( @PreviewsDayNight @Composable -internal fun LoginModeViewPreview(@PreviewParameter(ChangeServerErrorProvider::class) error: ChangeServerError) { +internal fun LoginModeViewPreview(@PreviewParameter(LoginModeViewErrorProvider::class) error: Throwable) { ElementPreview { LoginModeView( loginMode = AsyncData.Failure(error), diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeViewErrorProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeViewErrorProvider.kt new file mode 100644 index 0000000000..dd0a7f353c --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeViewErrorProvider.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2025 New Vector 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.login.impl.login + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.login.impl.error.ChangeServerErrorProvider +import io.element.android.libraries.matrix.api.auth.AuthenticationException + +class LoginModeViewErrorProvider : PreviewParameterProvider { + override val values: Sequence + get() = ChangeServerErrorProvider().values + + AuthenticationException.AccountAlreadyLoggedIn("@alice:matrix.org") +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/qrcode/QrCodeLoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/qrcode/QrCodeLoginFlowNode.kt index 2575d521ab..22dcb9da9c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/qrcode/QrCodeLoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/qrcode/QrCodeLoginFlowNode.kt @@ -23,7 +23,7 @@ import com.bumble.appyx.navmodel.backstack.operation.push import com.bumble.appyx.navmodel.backstack.operation.replace import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.di.QrCodeLoginBindings import io.element.android.features.login.impl.di.QrCodeLoginGraph @@ -49,7 +49,7 @@ import kotlinx.parcelize.Parcelize import timber.log.Timber @ContributesNode(AppScope::class) -@Inject +@AssistedInject class QrCodeLoginFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderNode.kt index 1efb7a6afc..a0587211c0 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderNode.kt @@ -16,12 +16,12 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.util.openLearnMorePage @ContributesNode(AppScope::class) -@Inject +@AssistedInject class ChangeAccountProviderNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt index e555a7d075..128d235c93 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt @@ -16,13 +16,13 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.util.openLearnMorePage import io.element.android.libraries.matrix.api.auth.OidcDetails @ContributesNode(AppScope::class) -@Inject +@AssistedInject class ChooseAccountProviderNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt index 2cab4bdbe9..0d50d21a18 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt @@ -16,7 +16,7 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.util.openLearnMorePage import io.element.android.libraries.architecture.NodeInputs @@ -24,7 +24,7 @@ import io.element.android.libraries.architecture.inputs import io.element.android.libraries.matrix.api.auth.OidcDetails @ContributesNode(AppScope::class) -@Inject +@AssistedInject class ConfirmAccountProviderNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 fb15191abe..d485755afb 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 @@ -13,12 +13,12 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.login.LoginHelper import io.element.android.libraries.architecture.Presenter -@Inject +@AssistedInject class ConfirmAccountProviderPresenter( @Assisted private val params: Params, private val accountProviderDataSource: AccountProviderDataSource, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountNode.kt index 94b249bc8e..44e4bde551 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountNode.kt @@ -16,7 +16,7 @@ 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.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab @@ -24,7 +24,7 @@ import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs @ContributesNode(AppScope::class) -@Inject +@AssistedInject class CreateAccountNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt index b713020acf..a3aec0eb81 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt @@ -15,7 +15,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.data.tryOrNull @@ -31,7 +31,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout import kotlin.time.Duration.Companion.seconds -@Inject +@AssistedInject class CreateAccountPresenter( @Assisted private val url: String, private val authenticationService: MatrixAuthenticationService, @@ -82,7 +82,7 @@ class CreateAccountPresenter( tryOrNull { // Wait until the session is verified val client = clientProvider.getOrRestore(sessionId).getOrThrow() - val sessionVerificationService = client.sessionVerificationService() + val sessionVerificationService = client.sessionVerificationService withTimeout(10.seconds) { sessionVerificationService.sessionVerifiedStatus.first { it.isVerified() } } } loggedInState.value = AsyncAction.Success(sessionId) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordNode.kt index e4a5992585..ce3d2a84fa 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordNode.kt @@ -14,11 +14,11 @@ 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.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode @ContributesNode(AppScope::class) -@Inject +@AssistedInject class LoginPasswordNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt index 29fed1adbd..3652a3df8d 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt @@ -16,7 +16,7 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.util.openLearnMorePage import io.element.android.libraries.architecture.NodeInputs @@ -24,7 +24,7 @@ import io.element.android.libraries.architecture.inputs import io.element.android.libraries.matrix.api.auth.OidcDetails @ContributesNode(AppScope::class) -@Inject +@AssistedInject class OnBoardingNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, @@ -97,6 +97,7 @@ class OnBoardingNode( onNeedLoginPassword = ::onLoginPasswordNeeded, onLearnMoreClick = { openLearnMorePage(context) }, onCreateAccountContinue = ::onCreateAccountContinue, + onBackClick = ::navigateUp, ) } } 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 cec124d587..e7e20aa70d 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 @@ -18,7 +18,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.appconfig.OnBoardingConfig import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.canConnectToAnyHomeserver @@ -27,9 +27,10 @@ import io.element.android.features.login.impl.login.LoginHelper import io.element.android.features.rageshake.api.RageshakeFeatureAvailability import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.ui.utils.MultipleTapToUnlock -@Inject +@AssistedInject class OnBoardingPresenter( @Assisted private val params: OnBoardingNode.Params, private val buildMeta: BuildMeta, @@ -38,6 +39,7 @@ class OnBoardingPresenter( private val rageshakeFeatureAvailability: RageshakeFeatureAvailability, private val loginHelper: LoginHelper, private val onBoardingLogoResIdProvider: OnBoardingLogoResIdProvider, + private val sessionStore: SessionStore, ) : Presenter { @AssistedFactory interface Factory { @@ -86,6 +88,10 @@ class OnBoardingPresenter( val onBoardingLogoResId = remember { onBoardingLogoResIdProvider.get() } + val isAddingAccount by produceState(initialValue = false) { + // We are adding an account if there is at least one session already stored + value = sessionStore.getAllSessions().isNotEmpty() + } val loginMode by loginHelper.collectLoginMode() @@ -109,6 +115,7 @@ class OnBoardingPresenter( } return OnBoardingState( + isAddingAccount = isAddingAccount, productionApplicationName = buildMeta.productionApplicationName, defaultAccountProvider = defaultAccountProvider, mustChooseAccountProvider = mustChooseAccountProvider, 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 c2896d4ea7..ae5bb79eb5 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 @@ -12,6 +12,7 @@ import io.element.android.features.login.impl.login.LoginMode import io.element.android.libraries.architecture.AsyncData data class OnBoardingState( + val isAddingAccount: Boolean, val productionApplicationName: String, val defaultAccountProvider: String?, val mustChooseAccountProvider: Boolean, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStateProvider.kt index cc41e64480..2eb9bfb301 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStateProvider.kt @@ -23,10 +23,16 @@ open class OnBoardingStateProvider : PreviewParameterProvider { anOnBoardingState(canLoginWithQrCode = true, canCreateAccount = true, canReportBug = true), anOnBoardingState(defaultAccountProvider = "element.io", canCreateAccount = false, canReportBug = true), anOnBoardingState(customLogoResId = R.drawable.sample_background), + anOnBoardingState( + isAddingAccount = true, + canLoginWithQrCode = true, + canCreateAccount = true, + ), ) } fun anOnBoardingState( + isAddingAccount: Boolean = false, productionApplicationName: String = "Element", defaultAccountProvider: String? = null, mustChooseAccountProvider: Boolean = false, @@ -39,6 +45,7 @@ fun anOnBoardingState( loginMode: AsyncData = AsyncData.Uninitialized, eventSink: (OnBoardingEvents) -> Unit = {}, ) = OnBoardingState( + isAddingAccount = isAddingAccount, productionApplicationName = productionApplicationName, defaultAccountProvider = defaultAccountProvider, mustChooseAccountProvider = mustChooseAccountProvider, 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 4c44ee132a..fbc4dc6d09 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 @@ -38,7 +38,9 @@ import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.atomic.atoms.ElementLogoAtom import io.element.android.libraries.designsystem.atomic.atoms.ElementLogoAtomSize import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule +import io.element.android.libraries.designsystem.atomic.pages.FlowStepPage import io.element.android.libraries.designsystem.atomic.pages.OnBoardingPage +import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button @@ -58,6 +60,7 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun OnBoardingView( state: OnBoardingState, + onBackClick: () -> Unit, onSignInWithQrCode: () -> Unit, onSignIn: (mustChooseAccountProvider: Boolean) -> Unit, onCreateAccount: () -> Unit, @@ -67,6 +70,52 @@ fun OnBoardingView( onCreateAccountContinue: (url: String) -> Unit, onReportProblem: () -> Unit, modifier: Modifier = Modifier, +) { + val loginView = @Composable { + LoginModeView( + loginMode = state.loginMode, + onClearError = { + state.eventSink(OnBoardingEvents.ClearError) + }, + onLearnMoreClick = onLearnMoreClick, + onOidcDetails = onOidcDetails, + onNeedLoginPassword = onNeedLoginPassword, + onCreateAccountContinue = onCreateAccountContinue, + ) + } + val buttons = @Composable { + OnBoardingButtons( + state = state, + onSignInWithQrCode = onSignInWithQrCode, + onSignIn = onSignIn, + onCreateAccount = onCreateAccount, + onReportProblem = onReportProblem, + ) + } + + if (state.isAddingAccount) { + AddOtherAccountScaffold( + modifier = modifier, + loginView = loginView, + buttons = buttons, + onBackClick = onBackClick, + ) + } else { + AddFirstAccountScaffold( + modifier = modifier, + state = state, + loginView = loginView, + buttons = buttons, + ) + } +} + +@Composable +private fun AddFirstAccountScaffold( + state: OnBoardingState, + loginView: @Composable () -> Unit, + buttons: @Composable () -> Unit, + modifier: Modifier = Modifier, ) { OnBoardingPage( modifier = modifier, @@ -79,29 +128,31 @@ fun OnBoardingView( } else { OnBoardingContent(state = state) } - LoginModeView( - loginMode = state.loginMode, - onClearError = { - state.eventSink(OnBoardingEvents.ClearError) - }, - onLearnMoreClick = onLearnMoreClick, - onOidcDetails = onOidcDetails, - onNeedLoginPassword = onNeedLoginPassword, - onCreateAccountContinue = onCreateAccountContinue, - ) + loginView() }, footer = { - OnBoardingButtons( - state = state, - onSignInWithQrCode = onSignInWithQrCode, - onSignIn = onSignIn, - onCreateAccount = onCreateAccount, - onReportProblem = onReportProblem, - ) + buttons() } ) } +@Composable +private fun AddOtherAccountScaffold( + loginView: @Composable () -> Unit, + buttons: @Composable () -> Unit, + onBackClick: () -> Unit, + modifier: Modifier = Modifier, +) { + FlowStepPage( + modifier = modifier, + title = stringResource(CommonStrings.common_add_account), + iconStyle = BigIcon.Style.Default(CompoundIcons.HomeSolid()), + buttons = { buttons() }, + content = loginView, + onBackClick = onBackClick, + ) +} + @Composable private fun OnBoardingContent(state: OnBoardingState) { Box( @@ -226,27 +277,29 @@ private fun OnBoardingButtons( .fillMaxWidth() ) } - if (state.canReportBug) { - // Add a report problem text button. Use a Text since we need a special theme here. - Text( - modifier = Modifier - .clickable(onClick = onReportProblem) - .padding(16.dp), - text = stringResource(id = CommonStrings.common_report_a_problem), - style = ElementTheme.typography.fontBodySmRegular, - color = ElementTheme.colors.textSecondary, - ) - } else { - Text( - modifier = Modifier - .clickable { - state.eventSink(OnBoardingEvents.OnVersionClick) - } - .padding(16.dp), - text = stringResource(id = R.string.screen_onboarding_app_version, state.version), - style = ElementTheme.typography.fontBodySmRegular, - color = ElementTheme.colors.textSecondary, - ) + if (state.isAddingAccount.not()) { + if (state.canReportBug) { + // Add a report problem text button. Use a Text since we need a special theme here. + Text( + modifier = Modifier + .clickable(onClick = onReportProblem) + .padding(16.dp), + text = stringResource(id = CommonStrings.common_report_a_problem), + style = ElementTheme.typography.fontBodySmRegular, + color = ElementTheme.colors.textSecondary, + ) + } else { + Text( + modifier = Modifier + .clickable { + state.eventSink(OnBoardingEvents.OnVersionClick) + } + .padding(16.dp), + text = stringResource(id = R.string.screen_onboarding_app_version, state.version), + style = ElementTheme.typography.fontBodySmRegular, + color = ElementTheme.colors.textSecondary, + ) + } } } } @@ -258,6 +311,7 @@ internal fun OnBoardingViewPreview( ) = ElementPreview { OnBoardingView( state = state, + onBackClick = {}, onSignInWithQrCode = {}, onSignIn = {}, onCreateAccount = {}, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationNode.kt index d7d9e7c826..8b8d5b2dd5 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationNode.kt @@ -14,13 +14,13 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.di.QrCodeLoginScope import io.element.android.libraries.architecture.inputs @ContributesNode(QrCodeLoginScope::class) -@Inject +@AssistedInject class QrCodeConfirmationNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorNode.kt index f7ebce1106..7b46c2e45c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorNode.kt @@ -14,7 +14,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.di.QrCodeLoginScope import io.element.android.features.login.impl.qrcode.QrCodeErrorScreenType @@ -22,7 +22,7 @@ import io.element.android.libraries.architecture.inputs import io.element.android.libraries.core.meta.BuildMeta @ContributesNode(QrCodeLoginScope::class) -@Inject +@AssistedInject class QrCodeErrorNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroNode.kt index 3e3a906a04..c86dc6096a 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroNode.kt @@ -14,12 +14,12 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.di.QrCodeLoginScope @ContributesNode(QrCodeLoginScope::class) -@Inject +@AssistedInject class QrCodeIntroNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanNode.kt index 0fd50bfcc3..f6b52522b5 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanNode.kt @@ -14,13 +14,13 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.di.QrCodeLoginScope import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData @ContributesNode(QrCodeLoginScope::class) -@Inject +@AssistedInject class QrCodeScanNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderNode.kt index 5011201b8b..dc6084032e 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderNode.kt @@ -16,12 +16,12 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.util.openLearnMorePage @ContributesNode(AppScope::class) -@Inject +@AssistedInject class SearchAccountProviderNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/login/impl/src/main/res/values-be/translations.xml b/features/login/impl/src/main/res/values-be/translations.xml index 6fde5f6622..307ccf7263 100644 --- a/features/login/impl/src/main/res/values-be/translations.xml +++ b/features/login/impl/src/main/res/values-be/translations.xml @@ -30,6 +30,7 @@ "Сардэчна запрашаем!" "Увайсці ў %1$s" "Увайсці ўручную" + "Увайсці ў %1$s" "Увайсці з QR-кодам" "Стварыць уліковы запіс" "Сардэчна запрашаем у самы хуткі %1$s. Перавага ў хуткасці і прастаце." diff --git a/features/login/impl/src/main/res/values-bg/translations.xml b/features/login/impl/src/main/res/values-bg/translations.xml index bd321a9c5a..6ccc7c9129 100644 --- a/features/login/impl/src/main/res/values-bg/translations.xml +++ b/features/login/impl/src/main/res/values-bg/translations.xml @@ -1,6 +1,7 @@ "Промяна на доставчика на акаунт" + "Адрес на сървъра" "Въведете термин за търсене или адрес на домейн." "Потърсете компания, общност или частен сървър." "Намерете доставчик на акаунт" @@ -8,18 +9,25 @@ "На път сте да влезете в %s" "Това е мястото, където ще живеят вашите разговори — точно както бихте използвали имейл доставчик, за да съхранявате вашите имейли." "На път сте да създадете акаунт в %s" + "Matrix.org е голям, безплатен сървър в публичната мрежа на Matrix за сигурна, децентрализирана комуникация, управляван от фондация Matrix.org." "Друг" "Използвайте друг доставчик на акаунт, като например собствен частен сървър или работен акаунт." "Промяна на доставчика на акаунт" + "Не можахме да достигнем този сървър. Моля, проверете дали сте въвели правилно URL адреса на сървъра. Ако URL адресът е правилен, свържете се с администратора на вашия сървър за допълнителна помощ." + "URL адрес на сървъра" "Какъв е адресът на вашия сървър?" + "Изберете своя сървър" "Създаване на акаунт" "Този акаунт бе деактивиран." "Неправилно потребителско име и/или парола" + "Това не е валиден потребителски идентификатор. Очакван формат: ‘@user:homeserver.org’" + "Избраният сървър не поддържа влизане с парола или OIDC. Моля, свържете се с вашия администратор или изберете друг сървър." "Въведете своите данни" "Matrix е отворена мрежа за сигурна, децентрализирана комуникация." "Добре дошли отново!" "Влизане в %1$s" "Влизане ръчно" + "Влизане в %1$s" "Влизане с QR код" "Създаване на акаунт" "Добре дошли в най-бързия %1$s досега. Супер зареден за скорост и простота." @@ -28,6 +36,7 @@ "Повторен опит" "Вашият код за потвърждение" "Промяна на доставчика на акаунт" + "Частен сървър за служителите на Element." "Matrix е отворена мрежа за сигурна, децентрализирана комуникация." "Това е мястото, където ще живеят вашите разговори — точно както бихте използвали имейл доставчик, за да съхранявате вашите имейли." "На път сте да влезете в %1$s" diff --git a/features/login/impl/src/main/res/values-cs/translations.xml b/features/login/impl/src/main/res/values-cs/translations.xml index e5d277c6eb..6ffd9a84d4 100644 --- a/features/login/impl/src/main/res/values-cs/translations.xml +++ b/features/login/impl/src/main/res/values-cs/translations.xml @@ -39,7 +39,7 @@ "Přihlaste se k %1$s" "Verze %1$s" "Ruční přihlášení" - "Přihlásit se do %1$s" + "Přihlaste se k %1$s" "Přihlásit se pomocí QR kódu" "Vytvořit účet" "Vítejte v dosud nejrychlejším %1$su. Vylepšený pro rychlost a jednoduchost." diff --git a/features/login/impl/src/main/res/values-de/translations.xml b/features/login/impl/src/main/res/values-de/translations.xml index da0da5257a..cb3459f62b 100644 --- a/features/login/impl/src/main/res/values-de/translations.xml +++ b/features/login/impl/src/main/res/values-de/translations.xml @@ -39,7 +39,7 @@ "Anmelden bei %1$s" "Version %1$s" "Manuell anmelden" - "Bei %1$s anmelden" + "Anmelden bei %1$s" "Mit QR-Code anmelden" "Konto erstellen" "Willkommen beim schnellsten %1$s aller Zeiten. Optimiert für Geschwindigkeit und Einfachheit." diff --git a/features/login/impl/src/main/res/values-eo/translations.xml b/features/login/impl/src/main/res/values-eo/translations.xml index 47e9b3830a..d8ecc9f5d6 100644 --- a/features/login/impl/src/main/res/values-eo/translations.xml +++ b/features/login/impl/src/main/res/values-eo/translations.xml @@ -1,4 +1,4 @@ - "A secure connection could not be made to the new device. Your existing connected devices are still safe and you don\'t need to worry about them." + "A secure connection could not be made to the new device. Your existing linked devices are still safe and you don\'t need to worry about them." diff --git a/features/login/impl/src/main/res/values-et/translations.xml b/features/login/impl/src/main/res/values-et/translations.xml index 83df34e10a..3e3e6add37 100644 --- a/features/login/impl/src/main/res/values-et/translations.xml +++ b/features/login/impl/src/main/res/values-et/translations.xml @@ -39,7 +39,7 @@ "Logi sisse serverisse %1$s" "Versioon %1$s" "Logi sisse käsitsi" - "Logi sisse teenusesse %1$s" + "Logi sisse serverisse %1$s" "Logi sisse QR-koodi alusel" "Loo kasutajakonto" "Läbi aegade kiireim ja mugavaim %1$s." diff --git a/features/login/impl/src/main/res/values-eu/translations.xml b/features/login/impl/src/main/res/values-eu/translations.xml index ae0079ad39..355e63546c 100644 --- a/features/login/impl/src/main/res/values-eu/translations.xml +++ b/features/login/impl/src/main/res/values-eu/translations.xml @@ -28,6 +28,7 @@ "Hasi saioa %1$s(e)n" "%1$s bertsioa" "Hasi saioa eskuz" + "Hasi saioa %1$s(e)n" "Hasi saioa QR kodearekin" "Sortu kontua" "Ongi etorri inoizko %1$s azkarrenera. Abiaduraz eta sinpletasunaz gainkargatua." diff --git a/features/login/impl/src/main/res/values-fr/translations.xml b/features/login/impl/src/main/res/values-fr/translations.xml index 26936afbd7..946fec802e 100644 --- a/features/login/impl/src/main/res/values-fr/translations.xml +++ b/features/login/impl/src/main/res/values-fr/translations.xml @@ -39,7 +39,7 @@ "Connectez-vous à %1$s" "Version %1$s" "Se connecter manuellement" - "Se connecter à %1$s" + "Connectez-vous à %1$s" "Se connecter avec un QR code" "Créer un compte" "Bienvenue dans l’application %1$s la plus rapide de tous les temps. Boosté pour plus de rapidité et de simplicité." diff --git a/features/login/impl/src/main/res/values-ka/translations.xml b/features/login/impl/src/main/res/values-ka/translations.xml index f3e46faf19..54e277ec12 100644 --- a/features/login/impl/src/main/res/values-ka/translations.xml +++ b/features/login/impl/src/main/res/values-ka/translations.xml @@ -29,6 +29,7 @@ "კეთილი იყოს თქვენი მობრძანება!" "შესვლა %1$s-ში" "ხელით შესვლა" + "შესვლა %1$s-ში" "შესვლა QR კოდით" "ანგარიშის შექმნა" "კეთილი იყოს თქვენი მობრძანება უსწრაფეს %1$s-ში. დამუხტულია სიჩქარისა და სიმარტივისათვის." diff --git a/features/login/impl/src/main/res/values-ko/translations.xml b/features/login/impl/src/main/res/values-ko/translations.xml index 2c8e688994..7c099f1759 100644 --- a/features/login/impl/src/main/res/values-ko/translations.xml +++ b/features/login/impl/src/main/res/values-ko/translations.xml @@ -39,7 +39,7 @@ "%1$s 에 로그인합니다" "버전 %1$s" "수동으로 로그인" - "%1$s 에 로그인하세요." + "%1$s 에 로그인합니다" "QR 코드로 로그인" "계정 만들기" "%1$s 에 오신 것을 환영합니다. 속도와 단순성을 극대화한 가장 빠른 버전입니다." diff --git a/features/login/impl/src/main/res/values-nl/translations.xml b/features/login/impl/src/main/res/values-nl/translations.xml index 03847a7ae9..8a7f695156 100644 --- a/features/login/impl/src/main/res/values-nl/translations.xml +++ b/features/login/impl/src/main/res/values-nl/translations.xml @@ -30,6 +30,7 @@ "Welkom terug!" "Inloggen bij %1$s" "Handmatig inloggen" + "Inloggen bij %1$s" "Inloggen met QR-code" "Account aanmaken" "Welkom bij de snelste %1$s ooit. Supercharged, voor snelheid en eenvoud." diff --git a/features/login/impl/src/main/res/values-pt/translations.xml b/features/login/impl/src/main/res/values-pt/translations.xml index a2a7bdc688..c2aa0d5ed6 100644 --- a/features/login/impl/src/main/res/values-pt/translations.xml +++ b/features/login/impl/src/main/res/values-pt/translations.xml @@ -39,7 +39,7 @@ "Iniciar sessão em %1$s" "Versão %1$s" "Iniciar sessão manualmente" - "Faz login em %1$s" + "Iniciar sessão em %1$s" "Iniciar sessão com código QR" "Criar conta" "Bem-vindo(a) à %1$s mais rápida de sempre. Super rápida e simples." diff --git a/features/login/impl/src/main/res/values-ru/translations.xml b/features/login/impl/src/main/res/values-ru/translations.xml index f362537644..8ce58135aa 100644 --- a/features/login/impl/src/main/res/values-ru/translations.xml +++ b/features/login/impl/src/main/res/values-ru/translations.xml @@ -13,6 +13,7 @@ "Другое" "Используйте другого поставщика учетных записей, например, собственный частный сервер или рабочую учетную запись." "Сменить поставщика учетной записи" + "Google Play" "Требуется приложение Element Pro для %1$s. Пожалуйста, загрузите его из магазина." "Требуется Element Pro" "Нам не удалось связаться с этим домашним сервером. Убедитесь, что вы правильно ввели URL-адрес домашнего сервера. Если URL-адрес указан правильно, обратитесь к администратору домашнего сервера за дополнительной помощью." diff --git a/features/login/impl/src/main/res/values-tr/translations.xml b/features/login/impl/src/main/res/values-tr/translations.xml index cfa7acf206..6d2bbeef13 100644 --- a/features/login/impl/src/main/res/values-tr/translations.xml +++ b/features/login/impl/src/main/res/values-tr/translations.xml @@ -30,6 +30,7 @@ "Tekrar hoş geldiniz!" "%1$s adresinde oturum aç" "Manuel olarak oturum aç" + "%1$s adresinde oturum aç" "QR kodu ile giriş yap" "Hesap oluştur" "Şimdiye kadarki en hızlı %1$s hoş geldiniz. Hız ve basitlik için güçlendirildi." diff --git a/features/login/impl/src/main/res/values-ur/translations.xml b/features/login/impl/src/main/res/values-ur/translations.xml index 7b1216415e..334d5b6ea6 100644 --- a/features/login/impl/src/main/res/values-ur/translations.xml +++ b/features/login/impl/src/main/res/values-ur/translations.xml @@ -30,6 +30,7 @@ "واپس خوش آمدید!" "%1$s میں داخل ہوں" "دستی طور پر داخل ہوں" + "%1$s میں داخل ہوں" "کیو آر (QR) رمز کیساتھ داخل ہوں" "کھاتہ تخلیق کریں" "اب تک کی تیز ترین %1$s میں خوش آمدید۔ رفتار اور سادگی کے لئے مشحون" diff --git a/features/login/impl/src/main/res/values-uz/translations.xml b/features/login/impl/src/main/res/values-uz/translations.xml index cbf065e925..373081f3c3 100644 --- a/features/login/impl/src/main/res/values-uz/translations.xml +++ b/features/login/impl/src/main/res/values-uz/translations.xml @@ -29,6 +29,7 @@ "Qaytib kelganingizdan xursandmiz!" "Kirish%1$s" "Qo\'lda tizimga kiring" + "Kirish%1$s" "QR kod bilan tizimga kiring" "Hisob yaratish" "Eng tezkor %1$sga xush kelibsiz. Tezlik va oddylik uchun super zaryadlangan." diff --git a/features/login/impl/src/main/res/values-zh-rTW/translations.xml b/features/login/impl/src/main/res/values-zh-rTW/translations.xml index 23e290d69c..1b0b94d7a6 100644 --- a/features/login/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/login/impl/src/main/res/values-zh-rTW/translations.xml @@ -39,7 +39,7 @@ "登入 %1$s" "版本 %1$s" "手動登入" - "登入至 %1$s" + "登入 %1$s" "使用 QR code 登入" "建立帳號" "歡迎使用有史以來最快的 %1$s。速度超快,操作簡便。" 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 b2f80e4ffe..3978d3be6e 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 @@ -117,7 +117,7 @@ class ConfirmAccountProviderPresenterTest { assertThat(successState.loginMode).isInstanceOf(AsyncData.Success::class.java) assertThat(successState.loginMode.dataOrNull()).isInstanceOf(LoginMode.Oidc::class.java) authenticationService.givenOidcCancelError(AN_EXCEPTION) - defaultOidcActionFlow.post(OidcAction.GoBack) + defaultOidcActionFlow.post(OidcAction.GoBack()) val cancelFailureState = awaitItem() assertThat(cancelFailureState.loginMode).isInstanceOf(AsyncData.Failure::class.java) } @@ -144,7 +144,30 @@ class ConfirmAccountProviderPresenterTest { assertThat(successState.submitEnabled).isFalse() assertThat(successState.loginMode).isInstanceOf(AsyncData.Success::class.java) assertThat(successState.loginMode.dataOrNull()).isInstanceOf(LoginMode.Oidc::class.java) - defaultOidcActionFlow.post(OidcAction.GoBack) + defaultOidcActionFlow.post(OidcAction.GoBack()) + val cancelFinalState = awaitItem() + assertThat(cancelFinalState.loginMode).isInstanceOf(AsyncData.Uninitialized::class.java) + } + } + + @Test + fun `present - oidc - cancel to unblock`() = runTest { + val authenticationService = FakeMatrixAuthenticationService() + val defaultOidcActionFlow = FakeOidcActionFlow() + val presenter = createConfirmAccountProviderPresenter( + matrixAuthenticationService = authenticationService, + defaultOidcActionFlow = defaultOidcActionFlow, + ) + authenticationService.givenHomeserver(A_HOMESERVER_OIDC) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) + val loadingState = awaitItem() + assertThat(loadingState.submitEnabled).isTrue() + assertThat(loadingState.loginMode).isInstanceOf(AsyncData.Loading::class.java) + defaultOidcActionFlow.post(OidcAction.GoBack(toUnblock = true)) val cancelFinalState = awaitItem() assertThat(cancelFinalState.loginMode).isInstanceOf(AsyncData.Uninitialized::class.java) } 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 17e8eb1dbd..16f6c649fa 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 @@ -29,6 +29,9 @@ import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationSer import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.oidc.api.OidcActionFlow import io.element.android.libraries.oidc.test.customtab.FakeOidcActionFlow +import io.element.android.libraries.sessionstorage.api.SessionStore +import io.element.android.libraries.sessionstorage.test.InMemorySessionStore +import io.element.android.libraries.sessionstorage.test.aSessionData import io.element.android.libraries.wellknown.api.WellknownRetriever import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.test @@ -79,10 +82,27 @@ class OnBoardingPresenterTest { assertThat(initialState.productionApplicationName).isEqualTo("B") assertThat(initialState.canCreateAccount).isEqualTo(OnBoardingConfig.CAN_CREATE_ACCOUNT) assertThat(initialState.canReportBug).isFalse() + assertThat(initialState.isAddingAccount).isFalse() assertThat(awaitItem().canLoginWithQrCode).isTrue() } } + @Test + fun `present - initial state adding account`() = runTest { + val presenter = createPresenter( + sessionStore = InMemorySessionStore( + initialList = listOf( + aSessionData() + ) + ) + ) + presenter.test { + skipItems(1) + val initialState = awaitItem() + assertThat(initialState.isAddingAccount).isTrue() + } + } + @Test fun `present - on boarding logo`() = runTest { val presenter = createPresenter( @@ -236,6 +256,7 @@ private fun createPresenter( rageshakeFeatureAvailability: () -> Flow = { flowOf(true) }, loginHelper: LoginHelper = createLoginHelper(), onBoardingLogoResIdProvider: OnBoardingLogoResIdProvider = OnBoardingLogoResIdProvider { null }, + sessionStore: SessionStore = InMemorySessionStore(), ) = OnBoardingPresenter( params = params, buildMeta = buildMeta, @@ -247,6 +268,7 @@ private fun createPresenter( rageshakeFeatureAvailability = rageshakeFeatureAvailability, loginHelper = loginHelper, onBoardingLogoResIdProvider = onBoardingLogoResIdProvider, + sessionStore = sessionStore, ) fun createLoginHelper( 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 8ac42b4c93..2f27e2fb2d 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 @@ -25,6 +25,7 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnceWithParam +import io.element.android.tests.testutils.pressBack import org.junit.Rule import org.junit.Test import org.junit.rules.TestRule @@ -50,6 +51,21 @@ class OnboardingViewTest { } } + @Test + fun `when can go back - clicking on back calls the expected callback`() { + val eventSink = EventsRecorder(expectEvents = false) + ensureCalledOnce { callback -> + rule.setOnboardingView( + state = anOnBoardingState( + isAddingAccount = true, + eventSink = eventSink, + ), + onBackClick = callback, + ) + rule.pressBack() + } + } + @Test fun `when can login with QR code - clicking on sign in with QR code calls the expected callback`() { val eventSink = EventsRecorder(expectEvents = false) @@ -235,6 +251,7 @@ class OnboardingViewTest { private fun AndroidComposeTestRule.setOnboardingView( state: OnBoardingState, + onBackClick: () -> Unit = EnsureNeverCalled(), onSignInWithQrCode: () -> Unit = EnsureNeverCalled(), onSignIn: (Boolean) -> Unit = EnsureNeverCalledWithParam(), onCreateAccount: () -> Unit = EnsureNeverCalled(), @@ -247,6 +264,7 @@ class OnboardingViewTest { setContent { OnBoardingView( state = state, + onBackClick = onBackClick, onSignInWithQrCode = onSignInWithQrCode, onSignIn = onSignIn, onCreateAccount = onCreateAccount, diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt index 245643cb9d..d554dbe8f0 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt @@ -14,13 +14,13 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.logout.api.LogoutEntryPoint import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class LogoutNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/voicemessages/composer/VoiceMessageComposerStateProvider.kt b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/voicemessages/composer/VoiceMessageComposerStateProvider.kt index e1bfee9a7f..b418b08726 100644 --- a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/voicemessages/composer/VoiceMessageComposerStateProvider.kt +++ b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/timeline/voicemessages/composer/VoiceMessageComposerStateProvider.kt @@ -10,7 +10,7 @@ package io.element.android.features.messages.api.timeline.voicemessages.composer import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.designsystem.components.media.createFakeWaveform import io.element.android.libraries.textcomposer.model.VoiceMessageState -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlin.time.Duration.Companion.seconds open class VoiceMessageComposerStateProvider : PreviewParameterProvider { @@ -42,4 +42,4 @@ fun aVoiceMessagePreviewState() = VoiceMessageState.Preview( waveform = createFakeWaveform(), ) -internal var aWaveformLevels = List(100) { it.toFloat() / 100 }.toPersistentList() +internal var aWaveformLevels = List(100) { it.toFloat() / 100 }.toImmutableList() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index 5cbce99701..82aafcf545 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -20,7 +20,7 @@ import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.Interaction import io.element.android.annotations.ContributesNode import io.element.android.features.call.api.CallType @@ -62,9 +62,9 @@ import io.element.android.libraries.dateformatter.api.DateFormatter import io.element.android.libraries.dateformatter.api.DateFormatterMode import io.element.android.libraries.dateformatter.api.toHumanReadableDuration import io.element.android.libraries.di.RoomScope -import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias @@ -73,6 +73,7 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.alias.matches import io.element.android.libraries.matrix.api.room.joinedRoomMembers +import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.matrix.ui.messages.RoomMemberProfilesCache @@ -91,11 +92,12 @@ import kotlinx.coroutines.withContext import kotlinx.parcelize.Parcelize @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class MessagesFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, - private val matrixClient: MatrixClient, + private val roomListService: RoomListService, + private val sessionId: SessionId, private val sendLocationEntryPoint: SendLocationEntryPoint, private val showLocationEntryPoint: ShowLocationEntryPoint, private val createPollEntryPoint: CreatePollEntryPoint, @@ -194,7 +196,7 @@ class MessagesFlowNode( } .launchIn(lifecycleScope) - matrixClient.roomListService + roomListService .allRooms .summaries .onEach { @@ -221,11 +223,13 @@ class MessagesFlowNode( } override fun onPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) { - backstack.push(NavTarget.AttachmentPreview( - attachment = attachments.first(), - timelineMode = Timeline.Mode.Live, - inReplyToEventId = inReplyToEventId, - )) + backstack.push( + NavTarget.AttachmentPreview( + attachment = attachments.first(), + timelineMode = Timeline.Mode.Live, + inReplyToEventId = inReplyToEventId, + ) + ) } override fun onUserDataClick(userId: UserId) { @@ -262,7 +266,7 @@ class MessagesFlowNode( override fun onJoinCallClick(roomId: RoomId) { val callType = CallType.RoomCall( - sessionId = matrixClient.sessionId, + sessionId = sessionId, roomId = roomId, ) analyticsService.captureInteraction(Interaction.Name.MobileRoomCallButton) @@ -348,18 +352,20 @@ class MessagesFlowNode( } is NavTarget.CreatePoll -> { createPollEntryPoint.nodeBuilder(this, buildContext) - .params(CreatePollEntryPoint.Params( - timelineMode = navTarget.timelineMode, - mode = CreatePollMode.NewPoll - )) + .params( + CreatePollEntryPoint.Params( + timelineMode = navTarget.timelineMode, + mode = CreatePollMode.NewPoll + ) + ) .build() } is NavTarget.EditPoll -> { createPollEntryPoint.nodeBuilder(this, buildContext) .params( CreatePollEntryPoint.Params( - timelineMode = navTarget.timelineMode, - mode = CreatePollMode.EditPoll(eventId = navTarget.eventId) + timelineMode = navTarget.timelineMode, + mode = CreatePollMode.EditPoll(eventId = navTarget.eventId) ) ) .build() @@ -412,11 +418,13 @@ class MessagesFlowNode( } override fun onPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) { - backstack.push(NavTarget.AttachmentPreview( - attachment = attachments.first(), - timelineMode = Timeline.Mode.Thread(navTarget.threadRootId), - inReplyToEventId = inReplyToEventId, - )) + backstack.push( + NavTarget.AttachmentPreview( + attachment = attachments.first(), + timelineMode = Timeline.Mode.Thread(navTarget.threadRootId), + inReplyToEventId = inReplyToEventId, + ) + ) } override fun onUserDataClick(userId: UserId) { @@ -453,12 +461,16 @@ class MessagesFlowNode( override fun onJoinCallClick(roomId: RoomId) { val callType = CallType.RoomCall( - sessionId = matrixClient.sessionId, + sessionId = sessionId, roomId = roomId, ) analyticsService.captureInteraction(Interaction.Name.MobileRoomCallButton) elementCallEntryPoint.startCall(callType) } + + override fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) { + backstack.push(NavTarget.OpenThread(threadRootId, focusedEventId)) + } } createNode(buildContext, listOf(inputs, callback)) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt index 250e271880..fc417fc029 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt @@ -21,6 +21,6 @@ interface MessagesNavigator { fun onReportContentClick(eventId: EventId, senderId: UserId) fun onEditPollClick(eventId: EventId) fun onPreviewAttachment(attachments: ImmutableList, inReplyToEventId: EventId?) - fun onNavigateToRoom(roomId: RoomId, serverNames: List) + fun onNavigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List) fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) } 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 1ab0885106..d4283d19d5 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 @@ -25,7 +25,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.compound.theme.ElementTheme import io.element.android.features.knockrequests.api.banner.KnockRequestsBannerRenderer @@ -74,7 +74,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class MessagesNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, @@ -92,6 +92,14 @@ class MessagesNode( private val knockRequestsBannerRenderer: KnockRequestsBannerRenderer, private val roomMemberModerationRenderer: RoomMemberModerationRenderer, ) : Node(buildContext, plugins = plugins), MessagesNavigator { + private val callbacks = plugins() + + data class Inputs( + val focusedEventId: EventId?, + ) : NodeInputs + + private val inputs = inputs() + private val timelineController = TimelineController(room, room.liveTimeline) private val presenter = presenterFactory.create( navigator = this, @@ -99,18 +107,12 @@ class MessagesNode( timelinePresenter = timelinePresenterFactory.create(timelineController = timelineController, this), actionListPresenter = actionListPresenterFactory.create( postProcessor = TimelineItemActionPostProcessor.Default, - timelineMode = timelineController.mainTimelineMode() + timelineMode = timelineController.mainTimelineMode(), ), timelineController = timelineController, ) - private val callbacks = plugins() - - data class Inputs(val focusedEventId: EventId?) : NodeInputs - - private val inputs = inputs() interface Callback : Plugin { - fun onRoomDetailsClick() fun onEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean fun onPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) fun onUserDataClick(userId: UserId) @@ -122,9 +124,10 @@ class MessagesNode( fun onCreatePollClick() fun onEditPollClick(eventId: EventId) fun onJoinCallClick(roomId: RoomId) + fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) + fun onRoomDetailsClick() fun onViewAllPinnedEvents() fun onViewKnockRequests() - fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) } override fun onBuilt() { @@ -143,6 +146,14 @@ class MessagesNode( callbacks.forEach { it.onRoomDetailsClick() } } + private fun onViewAllPinnedMessagesClick() { + callbacks.forEach { it.onViewAllPinnedEvents() } + } + + private fun onViewKnockRequestsClick() { + callbacks.forEach { it.onViewKnockRequests() } + } + private fun onEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean { // Note: cannot use `callbacks.all { it.onEventClick(event) }` because: // - if callbacks is empty, it will return true and we want to return false. @@ -223,11 +234,11 @@ class MessagesNode( callbacks.forEach { it.onPreviewAttachments(attachments, inReplyToEventId) } } - override fun onNavigateToRoom(roomId: RoomId, serverNames: List) { + override fun onNavigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List) { if (roomId == room.roomId) { displaySameRoomToast() } else { - val permalinkData = PermalinkData.RoomLink(roomId.toRoomIdOrAlias(), viaParameters = serverNames.toImmutableList()) + val permalinkData = PermalinkData.RoomLink(roomId.toRoomIdOrAlias(), eventId, viaParameters = serverNames.toImmutableList()) callbacks.forEach { it.onPermalinkClick(permalinkData) } } } @@ -236,10 +247,6 @@ class MessagesNode( callbacks.forEach { it.onOpenThread(threadRootId, focusedEventId) } } - private fun onViewAllPinnedMessagesClick() { - callbacks.forEach { it.onViewAllPinnedEvents() } - } - private fun onSendLocationClick() { callbacks.forEach { it.onSendLocationClick() } } @@ -252,10 +259,6 @@ class MessagesNode( callbacks.forEach { it.onJoinCallClick(room.roomId) } } - private fun onViewKnockRequestsClick() { - callbacks.forEach { it.onViewKnockRequests() } - } - private fun displaySameRoomToast() { context.toast(CommonStrings.screen_room_permalink_same_room_android) } @@ -291,7 +294,15 @@ class MessagesNode( } }, onUserDataClick = this::onUserDataClick, - onLinkClick = { url, customTab -> onLinkClick(activity, isDark, url, state.timelineState.eventSink, customTab) }, + onLinkClick = { url, customTab -> + onLinkClick( + activity = activity, + darkTheme = isDark, + url = url, + eventSink = state.timelineState.eventSink, + customTab = customTab, + ) + }, onSendLocationClick = this::onSendLocationClick, onCreatePollClick = this::onCreatePollClick, onJoinCallClick = this::onJoinCallClick, 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 04a456d71b..5bf92ef6f6 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 @@ -24,7 +24,7 @@ import androidx.compose.runtime.setValue import androidx.lifecycle.compose.LifecycleResumeEffect import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.PinUnpinAction import io.element.android.appconfig.MessageComposerConfig import io.element.android.features.messages.api.timeline.HtmlConverterProvider @@ -57,6 +57,7 @@ import io.element.android.libraries.androidutils.clipboard.ClipboardHelper import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.core.extensions.flatMap import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -70,6 +71,7 @@ import io.element.android.libraries.matrix.api.core.toThreadId import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.permalink.PermalinkParser +import io.element.android.libraries.matrix.api.recentemojis.AddRecentEmoji import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.RoomInfo @@ -87,13 +89,13 @@ import io.element.android.libraries.matrix.ui.room.getDirectRoomMember import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.api.AnalyticsService -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import timber.log.Timber -@Inject +@AssistedInject class MessagesPresenter( @Assisted private val navigator: MessagesNavigator, private val room: JoinedRoom, @@ -121,6 +123,7 @@ class MessagesPresenter( private val analyticsService: AnalyticsService, private val encryptionService: EncryptionService, private val featureFlagService: FeatureFlagService, + private val addRecentEmoji: AddRecentEmoji, ) : Presenter { @AssistedFactory interface Factory { @@ -163,7 +166,7 @@ class MessagesPresenter( derivedStateOf { roomInfo.avatarData() } } val heroes by remember { - derivedStateOf { roomInfo.heroes().toPersistentList() } + derivedStateOf { roomInfo.heroes().toImmutableList() } } var hasDismissedInviteDialog by rememberSaveable { @@ -282,8 +285,8 @@ class MessagesPresenter( } return produceState(UserEventPermissions.DEFAULT, key1 = key) { value = UserEventPermissions( - canSendMessage = room.canSendMessage(type = MessageEventType.ROOM_MESSAGE).getOrElse { true }, - canSendReaction = room.canSendMessage(type = MessageEventType.REACTION).getOrElse { true }, + canSendMessage = room.canSendMessage(type = MessageEventType.RoomMessage).getOrElse { true }, + canSendReaction = room.canSendMessage(type = MessageEventType.Reaction).getOrElse { true }, canRedactOwn = room.canRedactOwn().getOrElse { false }, canRedactOther = room.canRedactOther().getOrElse { false }, canPinUnpin = room.canPinUnpin().getOrElse { false }, @@ -398,6 +401,7 @@ class MessagesPresenter( ) = launch(dispatchers.io) { timelineController.invokeOnCurrentTimeline { toggleReaction(emoji, eventOrTransactionId) + .flatMap { added -> if (added) addRecentEmoji(emoji) else Result.success(Unit) } .onFailure { Timber.e(it) } } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index e20526d083..54b9dc659e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -48,6 +48,7 @@ import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.libraries.textcomposer.model.aTextEditorStateRich +import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentSetOf @@ -178,9 +179,11 @@ fun aReactionSummaryState( fun aCustomReactionState( target: CustomReactionState.Target = CustomReactionState.Target.None, + recentEmojis: ImmutableList = persistentListOf(), eventSink: (CustomReactionEvents) -> Unit = {}, ) = CustomReactionState( target = target, + recentEmojis = recentEmojis, selectedEmoji = persistentSetOf(), eventSink = eventSink, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt index 20dc45ccc8..25da8c2749 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt @@ -16,8 +16,8 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.ContributesBinding -import dev.zacsweers.metro.Inject import io.element.android.features.messages.impl.UserEventPermissions import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.impl.actionlist.model.TimelineItemActionComparator @@ -43,6 +43,7 @@ import io.element.android.libraries.di.RoomScope import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.recentemojis.GetRecentEmojis import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.preferences.api.store.AppPreferencesStore @@ -62,7 +63,7 @@ interface ActionListPresenter : Presenter { } } -@Inject +@AssistedInject class DefaultActionListPresenter( @Assisted private val postProcessor: TimelineItemActionPostProcessor, @@ -73,6 +74,7 @@ class DefaultActionListPresenter( private val userSendFailureFactory: VerifiedUserSendFailureFactory, private val dateFormatter: DateFormatter, private val featureFlagService: FeatureFlagService, + private val getRecentEmojis: GetRecentEmojis, ) : ActionListPresenter { @AssistedFactory @ContributesBinding(RoomScope::class) @@ -153,14 +155,15 @@ class DefaultActionListPresenter( ), displayEmojiReactions = displayEmojiReactions, verifiedUserSendFailure = verifiedUserSendFailure, - actions = actions.toImmutableList() + actions = actions.toImmutableList(), + recentEmojis = getRecentEmojis().getOrNull()?.toImmutableList() ?: persistentListOf() ) } else { target.value = ActionListState.Target.None } } - private suspend fun buildActions( + private fun buildActions( timelineItem: TimelineItem.Event, usersEventPermissions: UserEventPermissions, isDeveloperModeEnabled: Boolean, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListState.kt index 8082c3e415..7524a737ff 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListState.kt @@ -26,6 +26,7 @@ data class ActionListState( val event: TimelineItem.Event, val sentTimeFull: String, val displayEmojiReactions: Boolean, + val recentEmojis: ImmutableList, val verifiedUserSendFailure: VerifiedUserSendFailure, val actions: ImmutableList, ) : Target diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt index 28e62978de..eeab4aa3a0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt @@ -23,7 +23,8 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemVoiceContent import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList open class ActionListStateProvider : PreviewParameterProvider { override val values: Sequence @@ -41,6 +42,7 @@ open class ActionListStateProvider : PreviewParameterProvider { displayEmojiReactions = true, verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), + recentEmojis = persistentListOf(), ) ), anActionListState( @@ -56,6 +58,7 @@ open class ActionListStateProvider : PreviewParameterProvider { actions = aTimelineItemActionList( copyAction = TimelineItemAction.CopyCaption, ), + recentEmojis = persistentListOf(), ) ), anActionListState( @@ -70,6 +73,7 @@ open class ActionListStateProvider : PreviewParameterProvider { actions = aTimelineItemActionList( copyAction = TimelineItemAction.CopyCaption, ), + recentEmojis = persistentListOf(), ) ), anActionListState( @@ -84,6 +88,7 @@ open class ActionListStateProvider : PreviewParameterProvider { actions = aTimelineItemActionList( copyAction = null, ), + recentEmojis = persistentListOf(), ) ), anActionListState( @@ -98,6 +103,7 @@ open class ActionListStateProvider : PreviewParameterProvider { actions = aTimelineItemActionList( copyAction = TimelineItemAction.CopyCaption, ), + recentEmojis = persistentListOf(), ) ), anActionListState( @@ -112,6 +118,7 @@ open class ActionListStateProvider : PreviewParameterProvider { actions = aTimelineItemActionList( copyAction = null, ), + recentEmojis = persistentListOf(), ) ), anActionListState( @@ -124,6 +131,7 @@ open class ActionListStateProvider : PreviewParameterProvider { displayEmojiReactions = true, verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), + recentEmojis = persistentListOf(), ) ), anActionListState( @@ -136,6 +144,7 @@ open class ActionListStateProvider : PreviewParameterProvider { displayEmojiReactions = false, verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), + recentEmojis = persistentListOf(), ), ), anActionListState( @@ -148,6 +157,7 @@ open class ActionListStateProvider : PreviewParameterProvider { displayEmojiReactions = false, verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemPollActionList(), + recentEmojis = persistentListOf(), ), ), anActionListState( @@ -160,6 +170,7 @@ open class ActionListStateProvider : PreviewParameterProvider { displayEmojiReactions = true, verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = aTimelineItemActionList(), + recentEmojis = persistentListOf(), ) ), anActionListState( @@ -169,6 +180,7 @@ open class ActionListStateProvider : PreviewParameterProvider { displayEmojiReactions = true, verifiedUserSendFailure = anUnsignedDeviceSendFailure(), actions = aTimelineItemActionList(), + recentEmojis = persistentListOf(), ) ), ) @@ -197,7 +209,7 @@ fun aTimelineItemActionList( TimelineItemAction.ViewSource, ) .sortedWith(TimelineItemActionComparator()) - .toPersistentList() + .toImmutableList() } fun aTimelineItemPollActionList(): ImmutableList { @@ -210,5 +222,5 @@ fun aTimelineItemPollActionList(): ImmutableList { TimelineItemAction.Redact, ) .sortedWith(TimelineItemActionComparator()) - .toPersistentList() + .toImmutableList() } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt index bb57cc82d4..a891e9d587 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -20,9 +21,11 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredSize import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.ExperimentalMaterial3Api @@ -35,6 +38,10 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -90,6 +97,8 @@ import io.element.android.libraries.matrix.ui.messages.sender.SenderName import io.element.android.libraries.matrix.ui.messages.sender.SenderNameMode import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -218,6 +227,7 @@ private fun ActionListViewContent( if (target.displayEmojiReactions) { item { EmojiReactionsRow( + recentEmojis = target.recentEmojis, highlightedEmojis = target.event.reactionsState.highlightedKeys, onEmojiReactionClick = onEmojiReactionClick, onCustomReactionClick = onCustomReactionClick, @@ -335,43 +345,67 @@ private fun MessageSummary( } private val emojiRippleRadius = 24.dp +private val suggestedEmojis = persistentListOf("👍️", "👎️", "🔥", "❤️", "👏") @Composable private fun EmojiReactionsRow( + recentEmojis: ImmutableList, highlightedEmojis: ImmutableList, onEmojiReactionClick: (String) -> Unit, onCustomReactionClick: () -> Unit, modifier: Modifier = Modifier, ) { Row( - horizontalArrangement = Arrangement.SpaceBetween, - modifier = modifier.padding(horizontal = 24.dp, vertical = 16.dp) + modifier = modifier.padding(end = 16.dp, top = 16.dp, bottom = 16.dp), ) { - // TODO use most recently used emojis here when available from the Rust SDK - val defaultEmojis = sequenceOf( - "👍️", - "👎️", - "🔥", - "❤️", - "👏" - ) - for (emoji in defaultEmojis) { - val isHighlighted = highlightedEmojis.contains(emoji) - EmojiButton( - modifier = Modifier - // Make it appear after the more useful actions for the accessibility service - .semantics { - traversalIndex = 1f - }, - emoji = emoji, - isHighlighted = isHighlighted, - onClick = onEmojiReactionClick - ) + val backgroundColor = ElementTheme.colors.bgCanvasDefault + + val emojis = remember(recentEmojis) { + (suggestedEmojis + recentEmojis.filter { it !in suggestedEmojis }) + .take(100) + .toImmutableList() } - Box( + + LazyRow( modifier = Modifier - .size(48.dp), - contentAlignment = Alignment.Center, + .weight(1f, fill = true) + .drawWithContent { + val gradientWidth = 24.dp.toPx() + val width = size.width + drawContent() + + drawRect( + brush = Brush.horizontalGradient( + 0.0f to Color.Transparent, + 1.0f to backgroundColor, + startX = width - gradientWidth, + endX = width, + ), + topLeft = Offset(width - gradientWidth, 0f), + size = Size(gradientWidth, size.height) + ) + }, + contentPadding = PaddingValues(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + items(emojis) { emoji -> + val isHighlighted = highlightedEmojis.contains(emoji) + EmojiButton( + modifier = Modifier + // Make it appear after the more useful actions for the accessibility service + .semantics { + traversalIndex = 1f + }, + emoji = emoji, + isHighlighted = isHighlighted, + onClick = onEmojiReactionClick + ) + } + } + + Box( + modifier = Modifier.padding(end = 10.dp).requiredSize(48.dp), + contentAlignment = Alignment.CenterEnd, ) { Icon( imageVector = CompoundIcons.ReactionAdd(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt index 20697eea09..1df9969f72 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt @@ -13,7 +13,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.compound.theme.ForcedDarkElementTheme import io.element.android.features.messages.impl.attachments.Attachment @@ -25,7 +25,7 @@ import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.mediaviewer.api.local.LocalMediaRenderer @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class AttachmentsPreviewNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt index d33dbaf440..34a4f12fe5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt @@ -19,7 +19,7 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.video.MediaOptimizationSelectorPresenter import io.element.android.libraries.androidutils.file.TemporaryUriDeleter @@ -49,7 +49,7 @@ import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import timber.log.Timber -@Inject +@AssistedInject class AttachmentsPreviewPresenter( @Assisted private val attachment: Attachment, @Assisted private val onDoneListener: OnDoneListener, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenter.kt index 2b720882d2..b62b4116e6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenter.kt @@ -16,8 +16,8 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.ContributesBinding -import dev.zacsweers.metro.Inject import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo import io.element.android.libraries.di.SessionScope @@ -29,12 +29,12 @@ import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.preferences.api.store.SessionPreferencesStore import io.element.android.libraries.preferences.api.store.VideoCompressionPreset import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.first import timber.log.Timber import kotlin.math.roundToLong -@Inject +@AssistedInject class DefaultMediaOptimizationSelectorPresenter( @Assisted private val localMedia: LocalMedia, private val maxUploadSizeProvider: MaxUploadSizeProvider, @@ -111,7 +111,7 @@ class DefaultMediaOptimizationSelectorPresenter( canUpload = calculatedSize <= (maxUploadSize as AsyncData.Success).data ) } - .toPersistentList() + .toImmutableList() .also { sizes -> Timber.d(sizes.joinToString("\n") { "Calculated size for ${it.preset}: ${it.sizeInBytes} MB. Max upload size: $maxUploadSize" }) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/VideoMetadataExtractor.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/VideoMetadataExtractor.kt index 05287470ec..a63668acaa 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/VideoMetadataExtractor.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/VideoMetadataExtractor.kt @@ -14,8 +14,8 @@ import android.util.Size import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.ContributesBinding -import dev.zacsweers.metro.Inject import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.di.annotations.ApplicationContext import kotlin.time.Duration @@ -30,7 +30,7 @@ interface VideoMetadataExtractor : AutoCloseable { } @ContributesBinding(AppScope::class) -@Inject +@AssistedInject class DefaultVideoMetadataExtractor( @ApplicationContext private val context: Context, @Assisted private val uri: Uri, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesNode.kt index d03d034665..2cf0c6e1e7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesNode.kt @@ -18,7 +18,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.ParentNode import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs @@ -31,7 +31,7 @@ import io.element.android.libraries.roomselect.api.RoomSelectMode import kotlinx.parcelize.Parcelize @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class ForwardMessagesNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenter.kt index a52a5ca2cd..3e3860db3e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenter.kt @@ -12,7 +12,7 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState @@ -22,11 +22,11 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.timeline.TimelineProvider import io.element.android.libraries.matrix.api.timeline.getActiveTimeline import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -@Inject +@AssistedInject class ForwardMessagesPresenter( @Assisted eventId: String, @Assisted private val timelineProvider: TimelineProvider, @@ -43,7 +43,7 @@ class ForwardMessagesPresenter( private val forwardingActionState: MutableState>> = mutableStateOf(AsyncAction.Uninitialized) fun onRoomSelected(roomIds: List) { - sessionCoroutineScope.forwardEvent(eventId, roomIds.toPersistentList(), forwardingActionState) + sessionCoroutineScope.forwardEvent(eventId, roomIds.toImmutableList(), forwardingActionState) } @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index 36d85a5580..9505f0d758 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -26,7 +26,7 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshots.SnapshotStateList import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.Composer import im.vector.app.features.analytics.plan.Interaction import io.element.android.features.location.api.LocationService @@ -79,7 +79,7 @@ import io.element.android.services.analyticsproviders.api.trackers.captureIntera import io.element.android.wysiwyg.compose.RichTextEditorState import io.element.android.wysiwyg.display.TextDisplay import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.FlowPreview @@ -97,7 +97,7 @@ import timber.log.Timber import kotlin.time.Duration.Companion.seconds import io.element.android.libraries.core.mimetype.MimeTypes.Any as AnyMimeTypes -@Inject +@AssistedInject class MessageComposerPresenter( @Assisted private val navigator: MessagesNavigator, @Assisted private val timelineController: TimelineController, @@ -379,7 +379,7 @@ class MessageComposerPresenter( showAttachmentSourcePicker = showAttachmentSourcePicker, showTextFormatting = showTextFormatting, canShareLocation = canShareLocation.value, - suggestions = suggestions.toPersistentList(), + suggestions = suggestions.toImmutableList(), resolveMentionDisplay = resolveMentionDisplay, resolveAtRoomMentionDisplay = resolveAtRoomMentionDisplay, eventSink = { handleEvents(it) }, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt index 45898810cb..8ba776c520 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt @@ -19,7 +19,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.messages.impl.actionlist.ActionListPresenter import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories @@ -38,7 +38,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.ui.strings.CommonStrings @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class PinnedMessagesListNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt index 72389c891b..0c7fb8948a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt @@ -19,7 +19,7 @@ import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.Interaction import im.vector.app.features.analytics.plan.PinUnpinAction import io.element.android.features.messages.impl.UserEventPermissions @@ -61,7 +61,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import timber.log.Timber -@Inject +@AssistedInject class PinnedMessagesListPresenter( @Assisted private val navigator: PinnedMessagesListNavigator, private val room: JoinedRoom, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageNode.kt index 76d2d833ef..e40de20824 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageNode.kt @@ -13,7 +13,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs @@ -22,7 +22,7 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class ReportMessageNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenter.kt index 6bde64f3ad..90b6585718 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenter.kt @@ -17,7 +17,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runUpdatingState @@ -30,7 +30,7 @@ import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -@Inject +@AssistedInject class ReportMessagePresenter( private val room: JoinedRoom, @Assisted private val inputs: Inputs, 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 6f49b5b0f8..f732def95f 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 @@ -8,7 +8,6 @@ package io.element.android.features.messages.impl.threads import android.app.Activity -import android.content.Context import androidx.activity.compose.LocalActivity import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -25,7 +24,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.MessagesNavigator @@ -44,19 +43,18 @@ import io.element.android.features.messages.impl.timeline.di.TimelineItemPresent import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.androidutils.system.openUrlInExternalApp -import io.element.android.libraries.androidutils.system.toast import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.di.RoomScope -import io.element.android.libraries.di.annotations.ApplicationContext import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.matrix.api.analytics.toAnalyticsViewRoom 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.ThreadId import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkParser import io.element.android.libraries.matrix.api.room.CreateTimelineParams @@ -65,19 +63,18 @@ import io.element.android.libraries.matrix.api.room.alias.matches import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.mediaplayer.api.MediaPlayer -import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.api.AnalyticsService import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class ThreadedMessagesNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, - @ApplicationContext private val context: Context, @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, private val room: JoinedRoom, private val analyticsService: AnalyticsService, @@ -125,6 +122,7 @@ class ThreadedMessagesNode( fun onCreatePollClick() fun onEditPollClick(eventId: EventId) fun onJoinCallClick(roomId: RoomId) + fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) } override fun onBuilt() { @@ -191,8 +189,11 @@ class ThreadedMessagesNode( if (eventId != null) { eventSink(TimelineEvents.FocusOnEvent(eventId)) } else { - // Click on the same room, ignore - displaySameRoomToast() + // Click on the same room, navigate up + // Note that it can not be enough to go back to the room if the thread has been opened + // following a permalink from another thread. In this case navigating up will go back + // to the previous thread. But this should not happen often. + navigateUp() } } else { callbacks.forEach { it.onPermalinkClick(roomLink) } @@ -219,7 +220,14 @@ class ThreadedMessagesNode( callbacks.forEach { it.onPreviewAttachments(attachments, inReplyToEventId) } } - override fun onNavigateToRoom(roomId: RoomId, serverNames: List) = Unit + override fun onNavigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List) { + val permalinkData = PermalinkData.RoomLink(roomId.toRoomIdOrAlias(), eventId, viaParameters = serverNames.toImmutableList()) + callbacks.forEach { it.onPermalinkClick(permalinkData) } + } + + override fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) { + callbacks.forEach { it.onOpenThread(threadRootId, focusedEventId) } + } private fun onSendLocationClick() { callbacks.forEach { it.onSendLocationClick() } @@ -233,13 +241,6 @@ class ThreadedMessagesNode( callbacks.forEach { it.onJoinCallClick(room.roomId) } } - private fun displaySameRoomToast() { - context.toast(CommonStrings.screen_room_permalink_same_room_android) - } - - override fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) { - } - @Composable override fun View(modifier: Modifier) { val activity = requireNotNull(LocalActivity.current) @@ -273,11 +274,11 @@ class ThreadedMessagesNode( onUserDataClick = this::onUserDataClick, onLinkClick = { url, customTab -> onLinkClick( - activity, - isDark, - url, - state.timelineState.eventSink, - customTab + activity = activity, + darkTheme = isDark, + url = url, + eventSink = state.timelineState.eventSink, + customTab = customTab, ) }, onSendLocationClick = this::onSendLocationClick, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineController.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineController.kt index a0f4c2ce0e..779ebe984a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineController.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineController.kt @@ -14,6 +14,7 @@ import dev.zacsweers.metro.binding import io.element.android.features.messages.impl.timeline.di.LiveTimeline import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.room.CreateTimelineParams import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem @@ -74,21 +75,26 @@ class TimelineController( } } - suspend fun focusOnEvent(eventId: EventId): Result { - return room.createTimeline(CreateTimelineParams.Focused(eventId)) - .onFailure { - if (it is CancellationException) { - throw it - } - } - .map { newDetachedTimeline -> - detachedTimelineFlow.getAndUpdate { current -> - if (current.isPresent) { - current.get().close() + suspend fun focusOnEvent(eventId: EventId, threadRootId: ThreadId?): Result { + return if (threadRootId != null) { + Result.success(EventFocusResult.IsInThread(threadRootId)) + } else { + room.createTimeline(CreateTimelineParams.Focused(eventId)) + .onFailure { + if (it is CancellationException) { + throw it } - Optional.of(newDetachedTimeline) } - } + .map { newDetachedTimeline -> + detachedTimelineFlow.getAndUpdate { current -> + if (current.isPresent) { + current.get().close() + } + Optional.of(newDetachedTimeline) + } + EventFocusResult.FocusedOnLive + } + } } /** @@ -136,3 +142,8 @@ class TimelineController( return currentTimelineFlow } } + +sealed interface EventFocusResult { + data object FocusedOnLive : EventFocusResult + data class IsInThread(val threadId: ThreadId) : EventFocusResult +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index e3954e62a2..f03a1e8903 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -22,7 +22,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.features.messages.impl.MessagesNavigator import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureEvents import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureState @@ -44,6 +44,7 @@ import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UniqueId +import io.element.android.libraries.matrix.api.core.asEventId import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.isDm @@ -67,7 +68,7 @@ import timber.log.Timber const val FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS = 200L -@Inject +@AssistedInject class TimelinePresenter( timelineItemsFactoryCreator: TimelineItemsFactory.Creator, private val room: JoinedRoom, @@ -117,8 +118,8 @@ class TimelinePresenter( val syncUpdateFlow = room.syncUpdateFlow.collectAsState() - val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value) - val userHasPermissionToSendReaction by room.canSendMessageAsState(type = MessageEventType.REACTION, updateKey = syncUpdateFlow.value) + val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.RoomMessage, updateKey = syncUpdateFlow.value) + val userHasPermissionToSendReaction by room.canSendMessageAsState(type = MessageEventType.Reaction, updateKey = syncUpdateFlow.value) val prevMostRecentItemId = rememberSaveable { mutableStateOf(null) } @@ -207,7 +208,7 @@ class TimelinePresenter( is TimelineEvents.NavigateToPredecessorOrSuccessorRoom -> { // Navigate to the predecessor or successor room val serverNames = calculateServerNamesForRoom(room) - navigator.onNavigateToRoom(event.roomId, serverNames) + navigator.onNavigateToRoom(event.roomId, null, serverNames) } is TimelineEvents.OpenThread -> { navigator.onOpenThread( @@ -257,13 +258,39 @@ class TimelinePresenter( } is FocusRequestState.Loading -> { val eventId = currentFocusRequestState.eventId - timelineController.focusOnEvent(eventId) - .onSuccess { - focusRequestState = FocusRequestState.Success(eventId = eventId) - } - .onFailure { - focusRequestState = FocusRequestState.Failure(it) - } + val threadId = room.threadRootIdForEvent(eventId).getOrElse { + focusRequestState = FocusRequestState.Failure(it) + return@LaunchedEffect + } + + if (timelineController.mainTimelineMode() is Timeline.Mode.Thread && threadId == null) { + // We are in a thread timeline, and the event isn't part of a thread, we need to navigate back to the room + focusRequestState = FocusRequestState.None + navigator.onNavigateToRoom(room.roomId, eventId, calculateServerNamesForRoom(room)) + } else { + timelineController.focusOnEvent(eventId, threadId) + .onSuccess { result -> + when (result) { + is EventFocusResult.FocusedOnLive -> { + focusRequestState = FocusRequestState.Success(eventId = eventId) + } + is EventFocusResult.IsInThread -> { + val currentThreadId = (timelineController.mainTimelineMode() as? Timeline.Mode.Thread)?.threadRootId + if (currentThreadId == result.threadId) { + // It's the same thread, we just focus on the event + focusRequestState = FocusRequestState.Success(eventId = eventId) + } else { + focusRequestState = FocusRequestState.Success(eventId = result.threadId.asEventId()) + // It's part of a thread we're not in, let's open it in another timeline + navigator.onOpenThread(result.threadId, eventId) + } + } + } + } + .onFailure { + focusRequestState = FocusRequestState.Failure(it) + } + } } else -> Unit } @@ -341,7 +368,7 @@ class TimelinePresenter( newMostRecentItemId != prevMostRecentItemIdValue if (hasNewEvent) { - val newMostRecentEvent = newMostRecentItem as? TimelineItem.Event + val newMostRecentEvent = newMostRecentItem // Scroll to bottom if the new event is from me, even if sent from another device val fromMe = newMostRecentEvent?.isMine == true newEventState.value = if (fromMe) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index e7dc71f185..0cc61b4e4c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt @@ -41,7 +41,6 @@ import io.element.android.libraries.matrix.ui.messages.reply.aProfileTimelineDet import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toPersistentList import java.util.UUID import kotlin.random.Random @@ -197,7 +196,7 @@ fun aTimelineItemReactions( ) ) } - }.toPersistentList() + }.toImmutableList() ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt index 0ce8e02ecc..1a597fbda6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt @@ -74,7 +74,7 @@ fun TimelineItemGroupedEventsRow( ) }, ) { - val isExpanded = rememberSaveable(key = timelineItem.identifier().value) { mutableStateOf(false) } + val isExpanded = rememberSaveable { mutableStateOf(false) } fun onExpandGroupClick() { isExpanded.value = !isExpanded.value diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt index 2f66d586a9..84fbd2920a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt @@ -33,7 +33,7 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI import io.element.android.features.messages.impl.timeline.util.defaultTimelineContentPadding import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.persistentListOf @Composable fun TimelineItemStateEventRow( @@ -100,7 +100,7 @@ internal fun TimelineItemStateEventRowPreview() = ElementPreview { content = aTimelineItemStateEventContent(), groupPosition = TimelineItemGroupPosition.None, readReceiptState = TimelineItemReadReceipts( - receipts = listOf(aReadReceiptData(0)).toPersistentList(), + receipts = persistentListOf(aReadReceiptData(0)), ) ), renderReadReceipts = true, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt index 23abe8ea74..d0c848e393 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.Modifier import io.element.android.emojibasebindings.Emoji import io.element.android.features.messages.impl.timeline.components.customreaction.picker.EmojiPicker import io.element.android.features.messages.impl.timeline.components.customreaction.picker.EmojiPickerPresenter +import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet import io.element.android.libraries.designsystem.theme.components.hide import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId @@ -50,7 +51,13 @@ fun CustomReactionBottomSheet( sheetState = sheetState, modifier = modifier ) { - val presenter = remember { EmojiPickerPresenter(target.emojibaseStore) } + val presenter = remember { + EmojiPickerPresenter( + emojibaseStore = target.emojibaseStore, + recentEmojis = state.recentEmojis, + coroutineDispatchers = CoroutineDispatchers.Default, + ) + } EmojiPicker( onSelectEmoji = ::onEmojiSelectedDismiss, state = presenter.present(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt index b7d674d1de..ba13c461e4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt @@ -9,29 +9,39 @@ package io.element.android.features.messages.impl.timeline.components.customreac import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import dev.zacsweers.metro.Inject import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.matrix.api.recentemojis.GetRecentEmojis +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableSet import kotlinx.coroutines.launch @Inject class CustomReactionPresenter( - private val emojibaseProvider: EmojibaseProvider + private val emojibaseProvider: EmojibaseProvider, + private val getRecentEmojis: GetRecentEmojis, ) : Presenter { @Composable override fun present(): CustomReactionState { + val localCoroutineScope = rememberCoroutineScope() + var recentEmojis by remember { mutableStateOf>(persistentListOf()) } + val target: MutableState = remember { mutableStateOf(CustomReactionState.Target.None) } - val localCoroutineScope = rememberCoroutineScope() fun handleShowCustomReactionSheet(event: TimelineItem.Event) { target.value = CustomReactionState.Target.Loading(event) localCoroutineScope.launch { + recentEmojis = getRecentEmojis().getOrNull().orEmpty().toImmutableList() target.value = CustomReactionState.Target.Success( event = event, emojibaseStore = emojibaseProvider.emojibaseStore @@ -56,9 +66,11 @@ class CustomReactionPresenter( ?.mapNotNull { if (it.isHighlighted) it.key else null } .orEmpty() .toImmutableSet() + return CustomReactionState( target = target.value, selectedEmoji = selectedEmoji, + recentEmojis = recentEmojis, eventSink = { handleEvents(it) } ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionState.kt index 61fb0d7dde..9a9a985e62 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionState.kt @@ -9,11 +9,13 @@ package io.element.android.features.messages.impl.timeline.components.customreac import io.element.android.emojibasebindings.EmojibaseStore import io.element.android.features.messages.impl.timeline.model.TimelineItem +import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableSet data class CustomReactionState( val target: Target, val selectedEmoji: ImmutableSet, + val recentEmojis: ImmutableList, val eventSink: (CustomReactionEvents) -> Unit, ) { sealed interface Target { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPicker.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPicker.kt index 2bf6afafb5..83b21df092 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPicker.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPicker.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025 New Vector Ltd. + * Copyright 2023, 2024 New Vector Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial * Please see LICENSE files in the repository root for full details. @@ -30,16 +30,16 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.emojibasebindings.Emoji -import io.element.android.emojibasebindings.EmojibaseCategory import io.element.android.features.messages.impl.timeline.components.customreaction.EmojiItem import io.element.android.features.messages.impl.timeline.components.customreaction.icon -import io.element.android.features.messages.impl.timeline.components.customreaction.title import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.toSp import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.SearchBar import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.persistentSetOf import kotlinx.coroutines.launch @@ -53,9 +53,7 @@ fun EmojiPicker( modifier: Modifier = Modifier, ) { val coroutineScope = rememberCoroutineScope() - val categories = state.categories - val pagerState = rememberPagerState(pageCount = { EmojibaseCategory.entries.size }) - + val pagerState = rememberPagerState(pageCount = { state.categories.size }) Column(modifier) { SearchBar( modifier = Modifier.padding(bottom = 10.dp), @@ -66,36 +64,31 @@ fun EmojiPicker( onActiveChange = { state.eventSink(EmojiPickerEvents.ToggleSearchActive(it)) }, windowInsets = WindowInsets(0, 0, 0, 0), placeHolderTitle = stringResource(CommonStrings.emoji_picker_search_placeholder), - ) { results -> - val emojis = results - LazyVerticalGrid( - modifier = Modifier.fillMaxSize(), - columns = GridCells.Adaptive(minSize = 48.dp), - contentPadding = PaddingValues(vertical = 10.dp, horizontal = 16.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalArrangement = Arrangement.spacedBy(2.dp) - ) { - items(emojis, key = { it.unicode }) { item -> - SelectableEmojiItem( - item = item, - selectedEmojis = selectedEmojis, - onSelectEmoji = onSelectEmoji, - ) - } - } + ) { emojis -> + EmojiResults( + emojis = emojis, + isEmojiSelected = { selectedEmojis.contains(it.unicode) }, + onSelectEmoji = onSelectEmoji, + ) } if (!state.isSearchActive) { SecondaryTabRow( selectedTabIndex = pagerState.currentPage, ) { - EmojibaseCategory.entries.forEachIndexed { index, category -> + state.categories.forEachIndexed { index, category -> Tab( icon = { - Icon( - imageVector = category.icon, - contentDescription = stringResource(id = category.title) - ) + when (category.icon) { + is IconSource.Resource -> Icon( + resourceId = category.icon.id, + contentDescription = stringResource(id = category.titleId) + ) + is IconSource.Vector -> Icon( + imageVector = category.icon.vector, + contentDescription = stringResource(id = category.titleId) + ) + } }, selected = pagerState.currentPage == index, onClick = { @@ -109,41 +102,40 @@ fun EmojiPicker( state = pagerState, modifier = Modifier.fillMaxWidth(), ) { index -> - val category = EmojibaseCategory.entries[index] - val emojis = categories[category] ?: listOf() - LazyVerticalGrid( - modifier = Modifier.fillMaxSize(), - columns = GridCells.Adaptive(minSize = 48.dp), - contentPadding = PaddingValues(vertical = 10.dp, horizontal = 16.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalArrangement = Arrangement.spacedBy(2.dp) - ) { - items(emojis, key = { it.unicode }) { item -> - SelectableEmojiItem( - item = item, - selectedEmojis = selectedEmojis, - onSelectEmoji = onSelectEmoji, - ) - } - } + val emojis = state.categories[index].emojis + EmojiResults( + emojis = emojis, + isEmojiSelected = { selectedEmojis.contains(it.unicode) }, + onSelectEmoji = onSelectEmoji, + ) } } } } @Composable -private fun SelectableEmojiItem( - item: Emoji, - selectedEmojis: ImmutableSet, +private fun EmojiResults( + emojis: ImmutableList, + isEmojiSelected: (Emoji) -> Boolean, onSelectEmoji: (Emoji) -> Unit, ) { - EmojiItem( - modifier = Modifier.aspectRatio(1f), - item = item, - isSelected = selectedEmojis.contains(item.unicode), - onSelectEmoji = onSelectEmoji, - emojiSize = 32.dp.toSp(), - ) + LazyVerticalGrid( + modifier = Modifier.fillMaxSize(), + columns = GridCells.Adaptive(minSize = 48.dp), + contentPadding = PaddingValues(vertical = 10.dp, horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(2.dp) + ) { + items(emojis, key = { it.unicode }) { item -> + EmojiItem( + modifier = Modifier.aspectRatio(1f), + item = item, + isSelected = isEmojiSelected(item), + onSelectEmoji = onSelectEmoji, + emojiSize = 32.dp.toSp(), + ) + } + } } @PreviewsDayNight diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerPresenter.kt index de5b9f17a5..ce9600b1f7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerPresenter.kt @@ -14,26 +14,57 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalInspectionMode +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.emojibasebindings.Emoji import io.element.android.emojibasebindings.EmojibaseStore +import io.element.android.features.messages.impl.R +import io.element.android.features.messages.impl.timeline.components.customreaction.icon +import io.element.android.features.messages.impl.timeline.components.customreaction.title import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.withContext import kotlin.time.Duration.Companion.milliseconds class EmojiPickerPresenter( private val emojibaseStore: EmojibaseStore, + private val recentEmojis: ImmutableList, + private val coroutineDispatchers: CoroutineDispatchers, ) : Presenter { @Composable override fun present(): EmojiPickerState { var searchQuery by remember { mutableStateOf("") } var isSearchActive by remember { mutableStateOf(false) } var emojiResults by remember { mutableStateOf>>(SearchBarResultState.Initial()) } - val categories = remember { emojibaseStore.categories } + + val recentEmojiIcon = CompoundIcons.History() + val categories = remember { + val providedCategories = emojibaseStore.categories.map { (category, emojis) -> + EmojiCategory( + titleId = category.title, + icon = IconSource.Vector(category.icon), + emojis = emojis + ) + } + if (recentEmojis.isNotEmpty()) { + val recentEmojis = recentEmojis.mapNotNull { recentEmoji -> + emojibaseStore.allEmojis.find { it.unicode == recentEmoji } + }.toImmutableList() + val recentCategory = + EmojiCategory( + titleId = R.string.emoji_picker_category_recent, + icon = IconSource.Vector(recentEmojiIcon), + emojis = recentEmojis + ) + (listOf(recentCategory) + providedCategories).toImmutableList() + } else { + providedCategories.toImmutableList() + } + } LaunchedEffect(searchQuery) { emojiResults = if (searchQuery.isEmpty()) { @@ -43,7 +74,7 @@ class EmojiPickerPresenter( delay(100.milliseconds) val lowercaseQuery = searchQuery.lowercase() - val results = withContext(Dispatchers.Default) { + val results = withContext(coroutineDispatchers.computation) { emojibaseStore.allEmojis .asSequence() .filter { emoji -> @@ -71,6 +102,7 @@ class EmojiPickerPresenter( return EmojiPickerState( categories = categories, + allEmojis = emojibaseStore.allEmojis, searchQuery = searchQuery, isSearchActive = isSearchActive, searchResults = emojiResults, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerState.kt index 761f2f5bcd..595349a503 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerState.kt @@ -7,16 +7,26 @@ package io.element.android.features.messages.impl.timeline.components.customreaction.picker +import androidx.annotation.StringRes import io.element.android.emojibasebindings.Emoji -import io.element.android.emojibasebindings.EmojibaseCategory +import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.ImmutableMap data class EmojiPickerState( - val categories: ImmutableMap>, + val categories: ImmutableList, + val allEmojis: ImmutableList, val searchQuery: String, val isSearchActive: Boolean, val searchResults: SearchBarResultState>, val eventSink: (EmojiPickerEvents) -> Unit, ) + +/** + * Represents a category of emojis with a title id, icon, and the list of associated emojis. + */ +data class EmojiCategory( + @StringRes val titleId: Int, + val icon: IconSource, + val emojis: ImmutableList, +) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerStateProvider.kt index 5cff7cf9e0..f248efe893 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerStateProvider.kt @@ -10,11 +10,15 @@ package io.element.android.features.messages.impl.timeline.components.customreac import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.emojibasebindings.Emoji import io.element.android.emojibasebindings.EmojibaseCategory +import io.element.android.features.messages.impl.R +import io.element.android.features.messages.impl.timeline.components.customreaction.icon +import io.element.android.features.messages.impl.timeline.components.customreaction.title +import io.element.android.libraries.designsystem.icons.CompoundDrawables +import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableMap +import kotlinx.collections.immutable.toImmutableList class EmojiPickerStateProvider : PreviewParameterProvider { override val values: Sequence @@ -25,57 +29,52 @@ class EmojiPickerStateProvider : PreviewParameterProvider { anEmojiPickerState( isSearchActive = true, searchQuery = "smile", - searchResults = SearchBarResultState.Results( - persistentListOf( - Emoji( - "0x00", - "grinning face", - persistentListOf("grinning"), - persistentListOf("smile, grin"), - "😀", - null - ), - Emoji( - "0x01", - "crying face", - persistentListOf("crying"), - persistentListOf("smile, crying"), - "\uD83E\uDD72", - null - ), - ) - ) + searchResults = SearchBarResultState.Results(emojiList()) ), ) } +private fun recentEmojisCategory() = EmojiCategory( + titleId = R.string.emoji_picker_category_recent, + icon = IconSource.Resource(CompoundDrawables.ic_compound_history), + emojis = emojiList(), +) + +private fun emojiList(): ImmutableList = persistentListOf( + Emoji( + "0x00", + "grinning face", + persistentListOf("grinning"), + persistentListOf("smile, grin"), + "😀", + null + ), + Emoji( + "0x01", + "crying face", + persistentListOf("crying"), + persistentListOf("smile, crying"), + "\uD83E\uDD72", + null + ) +) + internal fun anEmojiPickerState( - categories: ImmutableMap> = EmojibaseCategory.entries.associateWith { - persistentListOf( - Emoji( - "0x00", - "grinning face", - persistentListOf("grinning"), - persistentListOf("smile, grin"), - "😀", - null - ), - Emoji( - "0x01", - "crying face", - persistentListOf("crying"), - persistentListOf("smile, crying"), - "\uD83E\uDD72", - null - ), + categories: ImmutableList = (listOf(recentEmojisCategory()) + EmojibaseCategory.entries.map { + EmojiCategory( + titleId = it.title, + icon = IconSource.Vector(it.icon), + emojis = emojiList(), ) - }.toImmutableMap(), + }).toImmutableList(), + allEmojis: ImmutableList = categories.flatMap { it.emojis }.toImmutableList(), searchQuery: String = "", isSearchActive: Boolean = false, searchResults: SearchBarResultState> = SearchBarResultState.Initial(), eventSink: (EmojiPickerEvents) -> Unit = {}, ) = EmojiPickerState( categories = categories, + allEmojis = allEmojis, searchQuery = searchQuery, isSearchActive = isSearchActive, searchResults = searchResults, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoNode.kt index 7cdf4acc00..d9e85c2eb2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoNode.kt @@ -13,7 +13,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs @@ -22,7 +22,7 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class EventDebugInfoNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt index a040b49e69..029373d42b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt @@ -9,7 +9,7 @@ package io.element.android.features.messages.impl.timeline.factories import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.features.messages.impl.timeline.diff.TimelineItemsCacheInvalidator import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemEventFactory import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory @@ -21,7 +21,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.distinctUntilChanged @@ -29,7 +29,7 @@ import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext -@Inject +@AssistedInject class TimelineItemsFactory( @Assisted config: TimelineItemsFactoryConfig, eventItemFactoryCreator: TimelineItemEventFactory.Creator, @@ -94,7 +94,7 @@ class TimelineItemsFactory( newTimelineItemStates.add(updatedItem) } } - val result = timelineItemGrouper.group(newTimelineItemStates).toPersistentList() + val result = timelineItemGrouper.group(newTimelineItemStates).toImmutableList() this._timelineItems.emit(result) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt index 8047acd09a..4f1c9ba90a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt @@ -13,6 +13,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRtcNotificationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.event.CallNotifyContent import io.element.android.libraries.matrix.api.timeline.item.event.EventContent @@ -31,7 +32,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.StickerConten import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent import io.element.android.libraries.matrix.api.timeline.item.event.getDisambiguatedDisplayName -import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder @Inject class TimelineItemContentFactory( @@ -45,7 +45,7 @@ class TimelineItemContentFactory( private val stateFactory: TimelineItemContentStateFactory, private val failedToParseMessageFactory: TimelineItemContentFailedToParseMessageFactory, private val failedToParseStateFactory: TimelineItemContentFailedToParseStateFactory, - private val currentSessionIdHolder: CurrentSessionIdHolder, + private val sessionId: SessionId, ) { suspend fun create(eventTimelineItem: EventTimelineItem): TimelineItemEventContent { return create( @@ -64,7 +64,7 @@ class TimelineItemContentFactory( sender: UserId, senderProfile: ProfileTimelineDetails, ): TimelineItemEventContent { - val isOutgoing = currentSessionIdHolder.current == sender + val isOutgoing = sessionId == sender return when (itemContent) { is FailedToParseMessageLikeContent -> failedToParseMessageFactory.create(itemContent) is FailedToParseStateContent -> failedToParseStateFactory.create(itemContent) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt index d3b3c5d679..6043cb57ff 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt @@ -9,7 +9,7 @@ package io.element.android.features.messages.impl.timeline.factories.event import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig import io.element.android.features.messages.impl.timeline.groups.canBeDisplayedInBubbleBlock import io.element.android.features.messages.impl.timeline.model.AggregatedReaction @@ -39,7 +39,7 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import java.util.Date -@Inject +@AssistedInject class TimelineItemEventFactory( @Assisted private val config: TimelineItemsFactoryConfig, private val contentFactory: TimelineItemContentFactory, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReactions.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReactions.kt index 84428790bc..115d8dab56 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReactions.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReactions.kt @@ -8,7 +8,7 @@ package io.element.android.features.messages.impl.timeline.model import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList data class TimelineItemReactions( val reactions: ImmutableList @@ -17,5 +17,5 @@ data class TimelineItemReactions( get() = reactions .filter { it.isHighlighted } .map { it.key } - .toPersistentList() + .toImmutableList() } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReactionsProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReactionsProvider.kt index c1f64efff4..b0084f20ee 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReactionsProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReactionsProvider.kt @@ -7,9 +7,9 @@ package io.element.android.features.messages.impl.timeline.model -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList fun aTimelineItemReactions() = TimelineItemReactions( // Use values from AggregatedReactionProvider - reactions = AggregatedReactionProvider().values.toPersistentList() + reactions = AggregatedReactionProvider().values.toImmutableList() ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentProvider.kt index 37c9a906bf..211bc5037b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentProvider.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.media.MediaSource -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.minutes @@ -53,7 +53,7 @@ fun aTimelineItemVoiceContent( duration = duration, mediaSource = mediaSource, mimeType = mimeType, - waveform = waveform.toPersistentList(), + waveform = waveform.toImmutableList(), formattedFileSize = "1.0 MB", fileExtension = "ogg", ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/MutableListExt.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/MutableListExt.kt deleted file mode 100644 index b59acde997..0000000000 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/MutableListExt.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2022-2024 New Vector 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.timeline.util - -internal inline fun MutableList.invalidateLast() { - val indexOfLast = size - if (indexOfLast > 0) { - set(indexOfLast - 1, null) - } -} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenter.kt index c8c33c52b2..7c5eeb4969 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenter.kt @@ -21,8 +21,8 @@ import androidx.core.net.toUri import androidx.lifecycle.Lifecycle import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.ContributesBinding -import dev.zacsweers.metro.Inject import im.vector.app.features.analytics.plan.Composer import io.element.android.features.messages.api.MessageComposerContext import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerEvents @@ -43,7 +43,6 @@ import io.element.android.libraries.voicerecorder.api.VoiceRecorderState import io.element.android.services.analytics.api.AnalyticsService import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import timber.log.Timber @@ -51,7 +50,7 @@ import java.io.File import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds -@Inject +@AssistedInject class DefaultVoiceMessageComposerPresenter( @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, @Assisted private val timelineMode: Timeline.Mode, @@ -199,7 +198,7 @@ class DefaultVoiceMessageComposerPresenter( voiceMessageState = when (val state = recorderState) { is VoiceRecorderState.Recording -> VoiceMessageState.Recording( duration = state.elapsedTime, - levels = state.levels.toPersistentList(), + levels = state.levels.toImmutableList(), ) is VoiceRecorderState.Finished -> previewState( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt index 5d9f4edfb5..2931f6af53 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt @@ -10,10 +10,10 @@ package io.element.android.features.messages.impl.voicemessages.timeline import androidx.compose.runtime.Composable import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.BindingContainer import dev.zacsweers.metro.Binds import dev.zacsweers.metro.ContributesTo -import dev.zacsweers.metro.Inject import dev.zacsweers.metro.IntoMap import io.element.android.features.messages.impl.timeline.di.TimelineItemEventContentKey import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactory @@ -32,7 +32,7 @@ interface VoiceMessagePresenterModule { fun bindVoiceMessagePresenterFactory(factory: VoiceMessagePresenter.Factory): TimelineItemPresenterFactory<*, *> } -@Inject +@AssistedInject class VoiceMessagePresenter( voiceMessagePresenterFactory: VoiceMessagePresenterFactory, @Assisted private val content: TimelineItemVoiceContent, diff --git a/features/messages/impl/src/main/res/values-bg/translations.xml b/features/messages/impl/src/main/res/values-bg/translations.xml index bd910f2e58..8efecfaf26 100644 --- a/features/messages/impl/src/main/res/values-bg/translations.xml +++ b/features/messages/impl/src/main/res/values-bg/translations.xml @@ -8,7 +8,15 @@ "Усмивки & Хора" "Пътуване & Места" "Символи" + "Докоснете, за да промените качеството на качване на видео" + "Файлът не можа да бъде качен." + "Неуспешна обработка на мултимедия за качване, моля, опитайте отново." + "Неуспешно качване на мултимедия, моля, опитайте отново." + "Файлът е твърде голям за качване" "Блокиране на потребителя" + "Отметнете ако искате да скриете всички настоящи и бъдещи съобщения от този потребител" + "Това съобщение ще бъде докладвано на администратора на вашия сървър. Те няма да могат да четат шифровани съобщения." + "Причина за докладване на това съдържание" "Камера" "Снимка" "Запис на видео" @@ -18,12 +26,17 @@ "Анкета" "Форматиране на текст" "Хронологията на съобщенията не е налична в момента." + "Искате ли да ги поканите обратно?" + "Вие сте сами в този чат" "Всеки" + "Изпращане отново" + "Вашето съобщение не успя да се изпрати" "Добавяне на емоджи" "Това е началото на %1$s." "Това е началото на този разговор." "Показване на по-малко" "Съобщението е копирано" + "Нямате разрешение да публикувате в тази стая" "Показване на по-малко" "Показване на повече" diff --git a/features/messages/impl/src/main/res/values-cy/translations.xml b/features/messages/impl/src/main/res/values-cy/translations.xml index 5591e3abd7..2d2c368fa3 100644 --- a/features/messages/impl/src/main/res/values-cy/translations.xml +++ b/features/messages/impl/src/main/res/values-cy/translations.xml @@ -54,6 +54,14 @@ "Ymatebodd %1$d aelod gyda %2$s" "Ymatebodd %1$d aelod gyda %2$s" + + "Fe wnaethoch chi a%1$d aelod ymateb gyda%2$s" + "Fe wnaethoch chi a%1$d aelod ymateb gyda%2$s" + "Fe wnaethoch chi a%1$d aelod ymateb gyda%2$s" + "Fe wnaethoch chi a%1$d aelod ymateb gyda%2$s" + "Fe wnaethoch chi a%1$d aelod ymateb gyda%2$s" + "Fe wnaethoch chi a%1$d aelod ymateb gyda%2$s" + "Rydych chi wedi ymateb gyda %1$s" "Dangos llai" "Dangos rhagor" diff --git a/features/messages/impl/src/main/res/values-da/translations.xml b/features/messages/impl/src/main/res/values-da/translations.xml index 5aba4e77c5..af7b0f003e 100644 --- a/features/messages/impl/src/main/res/values-da/translations.xml +++ b/features/messages/impl/src/main/res/values-da/translations.xml @@ -5,8 +5,9 @@ "Mad og drikke" "Dyr og natur" "Objekter" - "Smileys og mennesker" + "Smileys og personer" "Rejser og steder" + "Seneste emojis" "Symboler" "Billedtekster er muligvis ikke synlige for personer, der bruger ældre apps." "Tryk for at ændre videokvaliteten i uploadet" @@ -15,6 +16,7 @@ "Upload af medier mislykkedes. Prøv igen." "Den maksimalt tilladte filstørrelse er %1$s ." "Filen er for stor til at kunne uploades." + "Fil %1$d af %2$d" "Optimér billedkvaliteten" "Behandler…" "Bloker bruger" diff --git a/features/messages/impl/src/main/res/values-de/translations.xml b/features/messages/impl/src/main/res/values-de/translations.xml index f8db7fcafa..458be8a3b9 100644 --- a/features/messages/impl/src/main/res/values-de/translations.xml +++ b/features/messages/impl/src/main/res/values-de/translations.xml @@ -7,6 +7,7 @@ "Objekte" "Smileys & Menschen" "Reisen & Orte" + "Zuletzt verwendete Emojis" "Symbole" "Bildunterschriften sind für Nutzer älterer Apps möglicherweise nicht sichtbar." "Tippe, um die Qualität des Video-Uploads zu ändern" @@ -15,6 +16,7 @@ "Das Hochladen der Medien ist fehlgeschlagen. Bitte versuche es erneut." "Die maximal zulässige Dateigröße beträgt %1$s." "Die Datei ist zu groß zum Hochladen." + "%1$d von %2$d" "Optimiere die Bildqualität" "Verarbeitung läuft …" "Nutzer blockieren" diff --git a/features/messages/impl/src/main/res/values-et/translations.xml b/features/messages/impl/src/main/res/values-et/translations.xml index 3e07759a68..b2129f4ffd 100644 --- a/features/messages/impl/src/main/res/values-et/translations.xml +++ b/features/messages/impl/src/main/res/values-et/translations.xml @@ -7,6 +7,7 @@ "Esemed" "Emotikonid ja inimesed" "Reisimine ja kohad" + "Hiljutised emojid" "Sümbolid" "Selgitused ja alapealkirjad ei pruugi olla nähtavad vanemate rakenduste kasutajatele." "Klõpsa üleslaaditava video kvaliteedi muutmiseks" @@ -15,6 +16,7 @@ "Meediafaili üleslaadimine ei õnnestunud. Palun proovi uuesti." "Maksimaalne lubatud failisuurus on %1$s." "Fail on üleslaadimiseks liiga suur" + "Objekt %1$d/%2$d" "Optimeeri pildikvaliteeti" "Töötlen…" "Blokeeri kasutaja" diff --git a/features/messages/impl/src/main/res/values-fi/translations.xml b/features/messages/impl/src/main/res/values-fi/translations.xml index 3e1af3ce01..26c2c7fd65 100644 --- a/features/messages/impl/src/main/res/values-fi/translations.xml +++ b/features/messages/impl/src/main/res/values-fi/translations.xml @@ -16,6 +16,7 @@ "Median lähettäminen epäonnistui, yritä uudelleen." "Suurin sallittu tiedostokoko on %1$s." "Tiedosto on liian suuri lähetettäväksi" + "Kohde %1$d / %2$d" "Optimoi kuvanlaatu" "Käsitellään…" "Estä käyttäjä" diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml index 76b9ae22ee..cb8e65fd01 100644 --- a/features/messages/impl/src/main/res/values-fr/translations.xml +++ b/features/messages/impl/src/main/res/values-fr/translations.xml @@ -7,6 +7,7 @@ "Objets" "Émoticônes et personnes" "Voyages & lieux" + "Emojis récents" "Symboles" "Les légendes peuvent ne pas être visibles pour les utilisateurs d’anciennes applications." "Cliquez pour modifier la qualité d’envoi de la vidéo" @@ -15,6 +16,7 @@ "Échec du téléchargement du média, veuillez réessayer." "La taille maximale autorisée pour les fichiers est de %1$s." "Le fichier est trop volumineux pour être envoyé." + "Élément %1$d sur %2$d" "Optimiser la qualité de l’image" "Traitement en cours…" "Bloquer l’utilisateur" diff --git a/features/messages/impl/src/main/res/values-hu/translations.xml b/features/messages/impl/src/main/res/values-hu/translations.xml index d72afc0078..f859aa516a 100644 --- a/features/messages/impl/src/main/res/values-hu/translations.xml +++ b/features/messages/impl/src/main/res/values-hu/translations.xml @@ -7,6 +7,7 @@ "Tárgyak" "Mosolyok és emberek" "Utazás és helyek" + "Legutóbbi emodzsik" "Szimbólumok" "Előfordulhat, hogy a feliratok nem láthatók a régebbi alkalmazásokat használók számára." "Koppintson a feltöltött videók minőségének módosításához" @@ -15,6 +16,7 @@ "Nem sikerült a média feltöltése, próbálja újra." "A maximálisan megengedett fájlméret: %1$s ." "A fájl túl nagy a feltöltéshez" + "%1$d. elem / %2$d" "Képminőség optimalizációja" "Feldolgozás…" "Felhasználó letiltása" diff --git a/features/messages/impl/src/main/res/values-nb/translations.xml b/features/messages/impl/src/main/res/values-nb/translations.xml index deee637c09..5df3e5eac3 100644 --- a/features/messages/impl/src/main/res/values-nb/translations.xml +++ b/features/messages/impl/src/main/res/values-nb/translations.xml @@ -7,13 +7,17 @@ "Gjenstander" "Smilefjes og mennesker" "Reising og steder" + "Nylige emojier" "Symboler" "Teksting er kanskje ikke synlig for personer som bruker eldre apper." + "Trykk for å endre kvaliteten på videoopplastingen" "Filen kunne ikke lastes opp." "Kunne ikke behandle medier for opplasting, vennligst prøv igjen." "Opplasting av medier mislyktes, vennligst prøv igjen." "Maksimal tillatt filstørrelse er %1$s." "Filen er for stor til å lastes opp" + "Optimaliser bildekvaliteten" + "Behandler…" "Blokker bruker" "Kryss av for om du vil skjule alle nåværende og fremtidige meldinger fra denne brukeren" "Denne meldingen vil bli rapportert til hjemmeserverens administratorer. De vil ikke kunne lese noen krypterte meldinger." diff --git a/features/messages/impl/src/main/res/values-pt/translations.xml b/features/messages/impl/src/main/res/values-pt/translations.xml index 619f69e2da..3848f04063 100644 --- a/features/messages/impl/src/main/res/values-pt/translations.xml +++ b/features/messages/impl/src/main/res/values-pt/translations.xml @@ -7,6 +7,7 @@ "Objetos" "Caras e Pessoas" "Viagens e Lugares" + "Emojis recentes" "Símbolos" "As legendas poderão não ser visíveis em versões mais antigas da aplicação." "Toca para alterar a qualidade de carregamento do vídeo" @@ -15,6 +16,7 @@ "Falhar ao carregar multimédia, por favor tente novamente." "O tamanho máximo permitido é %1$s." "O ficheiro é demasiado grande para enviar" + "Item %1$d de %2$d" "Optimiza a qualidade da imagem" "A processar…" "Bloquear utilizador" diff --git a/features/messages/impl/src/main/res/values-ro/translations.xml b/features/messages/impl/src/main/res/values-ro/translations.xml index da8344f18a..a121bc7843 100644 --- a/features/messages/impl/src/main/res/values-ro/translations.xml +++ b/features/messages/impl/src/main/res/values-ro/translations.xml @@ -7,6 +7,7 @@ "Obiecte" "Fețe zâmbitoare & Oameni" "Călătorii & Locuri" + "Emoticoane recente" "Simboluri" "Este posibil ca descrierile să nu fie vizibile pentru persoanele care folosesc aplicații mai vechi." "Atingeți pentru a modifica calitatea încărcării videoclipului" @@ -15,6 +16,7 @@ "Încărcarea fișierelor media a eșuat, încercați din nou." "Dimensiunea maximă permisă pentru fișiere este de %1$s." "Fișierul este prea mare pentru a fi încărcat." + "Elementul %1$d din %2$d" "Optimizați calitatea imaginii" "Se procesează…" "Blocați utilizatorul" diff --git a/features/messages/impl/src/main/res/values-ru/translations.xml b/features/messages/impl/src/main/res/values-ru/translations.xml index 0cc79fba16..4656a5978b 100644 --- a/features/messages/impl/src/main/res/values-ru/translations.xml +++ b/features/messages/impl/src/main/res/values-ru/translations.xml @@ -7,10 +7,18 @@ "Объекты" "Улыбки и люди" "Путешествия и места" + "Недавние эмодзи" "Символы" "Подпись может быть не видна пользователям старых приложений." + "Нажмите, чтобы изменить качество загружаемого видео." + "Файл не может быть загружен." "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." "Не удалось загрузить медиафайлы, попробуйте еще раз." + "Максимальный размер файла: %1$s." + "Файл слишком большой для загрузки." + "Элемент %1$d из %2$d" + "Оптимизировать качество изображения" + "Обработка…" "Заблокировать пользователя" "Отметьте, хотите ли вы скрыть все текущие и будущие сообщения от этого пользователя" "Это сообщение будет передано администратору вашего домашнего сервера. Они не смогут прочитать зашифрованные сообщения." diff --git a/features/messages/impl/src/main/res/values-zh/translations.xml b/features/messages/impl/src/main/res/values-zh/translations.xml index 7bbab91d45..8d43d73632 100644 --- a/features/messages/impl/src/main/res/values-zh/translations.xml +++ b/features/messages/impl/src/main/res/values-zh/translations.xml @@ -7,6 +7,7 @@ "物品" "表情和人物" "旅行和地点" + "最近的 Emoji" "符号" "使用旧版应用程序的用户可能无法看到字幕。" "点按以更改视频上传质量" @@ -15,6 +16,7 @@ "上传媒体失败,请重试。" "允许的最大文件大小为%1$s 。" "文件太大,无法上传" + "第%1$d/%2$d项" "优化图像质量" "处理中…" "封禁用户" diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPointTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPointTest.kt index a4753807cd..2de761ad69 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPointTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPointTest.kt @@ -30,9 +30,10 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_USER_ID -import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.FakeBaseRoom +import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.libraries.matrix.ui.messages.RoomMemberProfilesCache import io.element.android.libraries.matrix.ui.messages.RoomNamesCache import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint @@ -60,7 +61,8 @@ class DefaultMessagesEntryPointTest { MessagesFlowNode( buildContext = buildContext, plugins = plugins, - matrixClient = FakeMatrixClient(), + roomListService = FakeRoomListService(), + sessionId = A_SESSION_ID, sendLocationEntryPoint = object : SendLocationEntryPoint { override fun builder(timelineMode: Timeline.Mode) = lambdaError() }, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt index 7320671694..bb59ca8551 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt @@ -22,7 +22,7 @@ class FakeMessagesNavigator( private val onReportContentClickLambda: (eventId: EventId, senderId: UserId) -> Unit = { _, _ -> lambdaError() }, private val onEditPollClickLambda: (eventId: EventId) -> Unit = { _ -> lambdaError() }, private val onPreviewAttachmentLambda: (attachments: ImmutableList, inReplyToEventId: EventId?) -> Unit = { _, _ -> lambdaError() }, - private val onNavigateToRoomLambda: (roomId: RoomId, serverNames: List) -> Unit = { _, _ -> lambdaError() }, + private val onNavigateToRoomLambda: (roomId: RoomId, threadId: EventId?, serverNames: List) -> Unit = { _, _, _ -> lambdaError() }, private val onOpenThreadLambda: (threadRootId: ThreadId, focusedEventId: EventId?) -> Unit = { _, _ -> lambdaError() }, ) : MessagesNavigator { override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { @@ -45,8 +45,8 @@ class FakeMessagesNavigator( onPreviewAttachmentLambda(attachments, inReplyToEventId) } - override fun onNavigateToRoom(roomId: RoomId, serverNames: List) { - onNavigateToRoomLambda(roomId, serverNames) + override fun onNavigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List) { + onNavigateToRoomLambda(roomId, eventId, serverNames) } override fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) { 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 52c5ee2259..56a3badf26 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 @@ -57,6 +57,7 @@ import io.element.android.libraries.matrix.api.core.toThreadId import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.permalink.PermalinkParser +import io.element.android.libraries.matrix.api.recentemojis.AddRecentEmoji import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.RoomMembersState import io.element.android.libraries.matrix.api.room.RoomMembershipState @@ -75,6 +76,7 @@ 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_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID_2 +import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser @@ -794,8 +796,8 @@ class MessagesPresenterTest { canUserPinUnpinResult = { Result.success(true) }, canUserSendMessageResult = { _, messageEventType -> when (messageEventType) { - MessageEventType.ROOM_MESSAGE -> Result.success(true) - MessageEventType.REACTION -> Result.success(true) + MessageEventType.RoomMessage -> Result.success(true) + MessageEventType.Reaction -> Result.success(true) else -> lambdaError() } }, @@ -820,8 +822,8 @@ class MessagesPresenterTest { canUserPinUnpinResult = { Result.success(true) }, canUserSendMessageResult = { _, messageEventType -> when (messageEventType) { - MessageEventType.ROOM_MESSAGE -> Result.success(false) - MessageEventType.REACTION -> Result.success(false) + MessageEventType.RoomMessage -> Result.success(false) + MessageEventType.Reaction -> Result.success(false) else -> lambdaError() } }, @@ -1269,6 +1271,7 @@ class MessagesPresenterTest { encryptionService: FakeEncryptionService = FakeEncryptionService(), featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(), actionListEventSink: (ActionListEvents) -> Unit = {}, + addRecentEmoji: AddRecentEmoji = AddRecentEmoji(FakeMatrixClient(), testCoroutineDispatchers()), ): MessagesPresenter { return MessagesPresenter( room = joinedRoom, @@ -1297,6 +1300,7 @@ class MessagesPresenterTest { encryptionService = encryptionService, analyticsService = analyticsService, featureFlagService = featureFlagService, + addRecentEmoji = addRecentEmoji, ) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt index c716b986b6..85027eb75e 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt @@ -370,6 +370,7 @@ class MessagesViewTest { displayEmojiReactions = true, actions = persistentListOf(TimelineItemAction.Edit), verifiedUserSendFailure = VerifiedUserSendFailure.None, + recentEmojis = persistentListOf(), ) ), ) @@ -462,6 +463,7 @@ class MessagesViewTest { displayEmojiReactions = true, verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf(TimelineItemAction.Edit), + recentEmojis = persistentListOf(), ), ), customReactionState = aCustomReactionState( @@ -491,6 +493,7 @@ class MessagesViewTest { displayEmojiReactions = true, verifiedUserSendFailure = aChangedIdentitySendFailure(), actions = persistentListOf(), + recentEmojis = persistentListOf(), ), ), timelineState = aTimelineState(eventSink = eventsRecorder) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt index e8766798cd..52118a400d 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt @@ -94,7 +94,8 @@ class ActionListPresenterTest { verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.ViewSource, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -135,7 +136,8 @@ class ActionListPresenterTest { verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.ViewSource, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -182,7 +184,8 @@ class ActionListPresenterTest { TimelineItemAction.CopyText, TimelineItemAction.ViewSource, TimelineItemAction.ReportContent, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -228,7 +231,8 @@ class ActionListPresenterTest { TimelineItemAction.CopyText, TimelineItemAction.ViewSource, TimelineItemAction.ReportContent, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -274,7 +278,8 @@ class ActionListPresenterTest { TimelineItemAction.CopyText, TimelineItemAction.ViewSource, TimelineItemAction.ReportContent, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -322,7 +327,8 @@ class ActionListPresenterTest { TimelineItemAction.ViewSource, TimelineItemAction.ReportContent, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -370,7 +376,8 @@ class ActionListPresenterTest { TimelineItemAction.ViewSource, TimelineItemAction.ReportContent, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -417,7 +424,8 @@ class ActionListPresenterTest { TimelineItemAction.CopyText, TimelineItemAction.ViewSource, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -463,7 +471,8 @@ class ActionListPresenterTest { TimelineItemAction.CopyText, TimelineItemAction.ViewSource, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -509,7 +518,8 @@ class ActionListPresenterTest { TimelineItemAction.Pin, TimelineItemAction.CopyText, TimelineItemAction.ViewSource, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -552,7 +562,8 @@ class ActionListPresenterTest { TimelineItemAction.CopyLink, TimelineItemAction.CopyText, TimelineItemAction.ViewSource, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -599,7 +610,8 @@ class ActionListPresenterTest { TimelineItemAction.Pin, TimelineItemAction.ViewSource, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -650,7 +662,8 @@ class ActionListPresenterTest { TimelineItemAction.RemoveCaption, TimelineItemAction.ViewSource, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -699,7 +712,8 @@ class ActionListPresenterTest { TimelineItemAction.CopyCaption, TimelineItemAction.ViewSource, TimelineItemAction.ReportContent, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -739,7 +753,8 @@ class ActionListPresenterTest { verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.ViewSource, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -812,7 +827,8 @@ class ActionListPresenterTest { TimelineItemAction.Pin, TimelineItemAction.CopyText, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -858,7 +874,8 @@ class ActionListPresenterTest { TimelineItemAction.CopyText, TimelineItemAction.ViewSource, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -911,7 +928,8 @@ class ActionListPresenterTest { TimelineItemAction.CopyText, TimelineItemAction.ViewSource, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) initialState.eventSink.invoke(ActionListEvents.Clear) @@ -1004,7 +1022,8 @@ class ActionListPresenterTest { TimelineItemAction.Edit, TimelineItemAction.CopyText, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) } @@ -1048,7 +1067,8 @@ class ActionListPresenterTest { TimelineItemAction.CopyLink, TimelineItemAction.Pin, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) } @@ -1091,7 +1111,8 @@ class ActionListPresenterTest { TimelineItemAction.CopyLink, TimelineItemAction.Pin, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) } @@ -1133,7 +1154,8 @@ class ActionListPresenterTest { TimelineItemAction.CopyLink, TimelineItemAction.Pin, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) } @@ -1178,7 +1200,8 @@ class ActionListPresenterTest { TimelineItemAction.CopyLink, TimelineItemAction.Pin, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) } @@ -1215,7 +1238,8 @@ class ActionListPresenterTest { verifiedUserSendFailure = VerifiedUserSendFailure.None, actions = persistentListOf( TimelineItemAction.ViewSource - ) + ), + recentEmojis = persistentListOf(), ) ) } @@ -1292,7 +1316,8 @@ class ActionListPresenterTest { TimelineItemAction.CopyLink, TimelineItemAction.Pin, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) } @@ -1345,7 +1370,8 @@ class ActionListPresenterTest { TimelineItemAction.CopyLink, TimelineItemAction.Pin, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) } @@ -1399,7 +1425,8 @@ class ActionListPresenterTest { TimelineItemAction.CopyLink, TimelineItemAction.Pin, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) } @@ -1450,7 +1477,8 @@ class ActionListPresenterTest { // Can't reply in thread for local events TimelineItemAction.Reply, TimelineItemAction.Redact, - ) + ), + recentEmojis = persistentListOf(), ) ) } @@ -1472,5 +1500,6 @@ private fun createActionListPresenter( dateFormatter = FakeDateFormatter(), timelineMode = timelineMode, featureFlagService = featureFlagService, + getRecentEmojis = { Result.success(persistentListOf()) }, ) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/TimelineItemsFactoryFixtures.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/TimelineItemsFactoryFixtures.kt index 502ab74062..539618df9f 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/TimelineItemsFactoryFixtures.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/TimelineItemsFactoryFixtures.kt @@ -33,7 +33,6 @@ import io.element.android.libraries.dateformatter.test.FakeDateFormatter import io.element.android.libraries.eventformatter.api.TimelineEventFormatter import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.event.EventContent -import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.mediaviewer.test.util.FileExtensionExtractorWithoutValidation @@ -78,7 +77,7 @@ internal fun TestScope.aTimelineItemsFactory( stateFactory = TimelineItemContentStateFactory(timelineEventFormatter), failedToParseMessageFactory = TimelineItemContentFailedToParseMessageFactory(), failedToParseStateFactory = TimelineItemContentFailedToParseStateFactory(), - currentSessionIdHolder = CurrentSessionIdHolder(matrixClient), + sessionId = matrixClient.sessionId, ), matrixClient = matrixClient, dateFormatter = FakeDateFormatter(), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineControllerTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineControllerTest.kt index 71d295ba8b..524cb3e1e8 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineControllerTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineControllerTest.kt @@ -40,7 +40,7 @@ class TimelineControllerTest { assertThat(state).isEqualTo(liveTimeline) } assertThat(sut.isLive().first()).isTrue() - sut.focusOnEvent(AN_EVENT_ID) + sut.focusOnEvent(AN_EVENT_ID, null) awaitItem().also { state -> assertThat(state).isEqualTo(detachedTimeline) } @@ -78,14 +78,14 @@ class TimelineControllerTest { awaitItem().also { state -> assertThat(state).isEqualTo(liveTimeline) } - sut.focusOnEvent(AN_EVENT_ID) + sut.focusOnEvent(AN_EVENT_ID, null) awaitItem().also { state -> assertThat(state).isEqualTo(detachedTimeline1) } assertThat(detachedTimeline1.closeCounter).isEqualTo(0) assertThat(detachedTimeline2.closeCounter).isEqualTo(0) // Focus on another event should close the previous detached timeline - sut.focusOnEvent(AN_EVENT_ID) + sut.focusOnEvent(AN_EVENT_ID, null) awaitItem().also { state -> assertThat(state).isEqualTo(detachedTimeline2) } @@ -124,7 +124,7 @@ class TimelineControllerTest { awaitItem().also { state -> assertThat(state).isEqualTo(liveTimeline) } - sut.focusOnEvent(AN_EVENT_ID) + sut.focusOnEvent(AN_EVENT_ID, null) awaitItem().also { state -> assertThat(state).isEqualTo(detachedTimeline) } @@ -171,11 +171,11 @@ class TimelineControllerTest { ) val sut = TimelineController(room = joinedRoom, liveTimeline = liveTimeline) sut.activeTimelineFlow().test { - sut.focusOnEvent(AN_EVENT_ID) + sut.focusOnEvent(AN_EVENT_ID, null) awaitItem().also { state -> assertThat(state).isEqualTo(liveTimeline) } - sut.focusOnEvent(AN_EVENT_ID) + sut.focusOnEvent(AN_EVENT_ID, null) awaitItem().also { state -> assertThat(state).isEqualTo(detachedTimeline) } @@ -200,7 +200,7 @@ class TimelineControllerTest { awaitItem().also { state -> assertThat(state).isEqualTo(liveTimeline) } - sut.focusOnEvent(AN_EVENT_ID) + sut.focusOnEvent(AN_EVENT_ID, null) awaitItem().also { state -> assertThat(state).isEqualTo(detachedTimeline) } 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 8710af8bda..8da614f67e 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 @@ -31,7 +31,9 @@ import io.element.android.features.roomcall.api.aStandByCallState import io.element.android.libraries.featureflag.test.FakeFeatureFlagService 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.ThreadId import io.element.android.libraries.matrix.api.core.UniqueId +import io.element.android.libraries.matrix.api.core.asEventId import io.element.android.libraries.matrix.api.room.RoomMembersState import io.element.android.libraries.matrix.api.room.tombstone.PredecessorRoom import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem @@ -44,6 +46,8 @@ import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTime 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_THREAD_ID +import io.element.android.libraries.matrix.test.A_THREAD_ID_2 import io.element.android.libraries.matrix.test.A_UNIQUE_ID import io.element.android.libraries.matrix.test.A_UNIQUE_ID_2 import io.element.android.libraries.matrix.test.A_USER_ID @@ -535,7 +539,10 @@ class TimelinePresenterTest { val room = FakeJoinedRoom( liveTimeline = liveTimeline, createTimelineResult = { Result.success(detachedTimeline) }, - baseRoom = FakeBaseRoom(canUserSendMessageResult = { _, _ -> Result.success(true) }), + baseRoom = FakeBaseRoom( + canUserSendMessageResult = { _, _ -> Result.success(true) }, + threadRootIdForEventResult = { _ -> Result.success(null) }, + ), ) val presenter = createTimelinePresenter( room = room, @@ -613,7 +620,10 @@ class TimelinePresenterTest { timelineItems = flowOf(emptyList()), ), createTimelineResult = { Result.failure(RuntimeException("An error")) }, - baseRoom = FakeBaseRoom(canUserSendMessageResult = { _, _ -> Result.success(true) }), + baseRoom = FakeBaseRoom( + canUserSendMessageResult = { _, _ -> Result.success(true) }, + threadRootIdForEventResult = { _ -> Result.success(null) }, + ), ) ) moleculeFlow(RecompositionMode.Immediate) { @@ -639,6 +649,246 @@ class TimelinePresenterTest { } } + @Test + fun `present - focus on event in a thread opens the thread`() = runTest { + val threadId = A_THREAD_ID + val detachedTimeline = FakeTimeline( + mode = Timeline.Mode.FocusedOnEvent(AN_EVENT_ID_2), + timelineItems = flowOf( + listOf( + MatrixTimelineItem.Event( + uniqueId = A_UNIQUE_ID, + event = anEventTimelineItem(), + ) + ) + ) + ) + val liveTimeline = FakeTimeline( + timelineItems = flowOf(emptyList()) + ) + val room = FakeJoinedRoom( + liveTimeline = liveTimeline, + createTimelineResult = { Result.success(detachedTimeline) }, + baseRoom = FakeBaseRoom( + canUserSendMessageResult = { _, _ -> Result.success(true) }, + threadRootIdForEventResult = { _ -> Result.success(threadId) }, + ), + ) + val openThreadLambda = lambdaRecorder { _: ThreadId, _: EventId? -> } + val navigator = FakeMessagesNavigator(onOpenThreadLambda = openThreadLambda) + val presenter = createTimelinePresenter( + room = room, + timeline = liveTimeline, + messagesNavigator = navigator, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitFirstItem() + initialState.eventSink.invoke(TimelineEvents.FocusOnEvent(AN_EVENT_ID)) + + awaitItem().also { state -> + assertThat(state.focusedEventId).isEqualTo(AN_EVENT_ID) + assertThat(state.focusRequestState).isEqualTo(FocusRequestState.Requested(AN_EVENT_ID, Duration.ZERO)) + } + + advanceUntilIdle() + + assertThat(awaitItem().focusRequestState).isEqualTo(FocusRequestState.Loading(AN_EVENT_ID)) + + // The live timeline focuses in the thread root + assertThat(awaitItem().focusRequestState).isEqualTo(FocusRequestState.Success(A_THREAD_ID.asEventId())) + + // The thread is opened + openThreadLambda.assertions() + .isCalledOnce() + .with( + value(threadId), + value(AN_EVENT_ID), + ) + } + } + + @Test + fun `present - focus on event in a thread when in the same thread just moves the focus`() = runTest { + val threadId = A_THREAD_ID + val detachedTimeline = FakeTimeline( + mode = Timeline.Mode.FocusedOnEvent(AN_EVENT_ID_2), + timelineItems = flowOf( + listOf( + MatrixTimelineItem.Event( + uniqueId = A_UNIQUE_ID, + event = anEventTimelineItem(), + ) + ) + ) + ) + val liveTimeline = FakeTimeline( + mode = Timeline.Mode.Thread(threadId), + timelineItems = flowOf(emptyList()) + ) + val room = FakeJoinedRoom( + liveTimeline = liveTimeline, + createTimelineResult = { Result.success(detachedTimeline) }, + baseRoom = FakeBaseRoom( + canUserSendMessageResult = { _, _ -> Result.success(true) }, + threadRootIdForEventResult = { _ -> Result.success(threadId) }, + ), + ) + val openThreadLambda = lambdaRecorder { _: ThreadId, _: EventId? -> } + val navigator = FakeMessagesNavigator(onOpenThreadLambda = openThreadLambda) + val presenter = createTimelinePresenter( + room = room, + timeline = liveTimeline, + messagesNavigator = navigator, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitFirstItem() + initialState.eventSink.invoke(TimelineEvents.FocusOnEvent(AN_EVENT_ID)) + + awaitItem().also { state -> + assertThat(state.focusedEventId).isEqualTo(AN_EVENT_ID) + assertThat(state.focusRequestState).isEqualTo(FocusRequestState.Requested(AN_EVENT_ID, Duration.ZERO)) + } + + advanceUntilIdle() + + assertThat(awaitItem().focusRequestState).isEqualTo(FocusRequestState.Loading(AN_EVENT_ID)) + + // The live timeline focuses in the event directly since we are already in the thread + assertThat(awaitItem().focusRequestState).isEqualTo(FocusRequestState.Success(AN_EVENT_ID)) + + // The thread is not opened again + openThreadLambda.assertions().isNeverCalled() + } + } + + @Test + fun `present - focus on event in a thread when in a different thread opens the new thread`() = runTest { + val currentThreadId = A_THREAD_ID + val detachedTimeline = FakeTimeline( + mode = Timeline.Mode.FocusedOnEvent(AN_EVENT_ID_2), + timelineItems = flowOf( + listOf( + MatrixTimelineItem.Event( + uniqueId = A_UNIQUE_ID, + event = anEventTimelineItem(), + ) + ) + ) + ) + val liveTimeline = FakeTimeline( + mode = Timeline.Mode.Thread(currentThreadId), + timelineItems = flowOf(emptyList()) + ) + val room = FakeJoinedRoom( + liveTimeline = liveTimeline, + createTimelineResult = { Result.success(detachedTimeline) }, + baseRoom = FakeBaseRoom( + canUserSendMessageResult = { _, _ -> Result.success(true) }, + // Use a different thread id + threadRootIdForEventResult = { _ -> Result.success(A_THREAD_ID_2) }, + ), + ) + val openThreadLambda = lambdaRecorder { _: ThreadId, _: EventId? -> } + val navigator = FakeMessagesNavigator(onOpenThreadLambda = openThreadLambda) + val presenter = createTimelinePresenter( + room = room, + timeline = liveTimeline, + messagesNavigator = navigator, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitFirstItem() + initialState.eventSink.invoke(TimelineEvents.FocusOnEvent(AN_EVENT_ID)) + + awaitItem().also { state -> + assertThat(state.focusedEventId).isEqualTo(AN_EVENT_ID) + assertThat(state.focusRequestState).isEqualTo(FocusRequestState.Requested(AN_EVENT_ID, Duration.ZERO)) + } + + advanceUntilIdle() + + assertThat(awaitItem().focusRequestState).isEqualTo(FocusRequestState.Loading(AN_EVENT_ID)) + + // The live timeline focuses in the event directly since we are already in the thread + assertThat(awaitItem().focusRequestState).isEqualTo(FocusRequestState.Success(A_THREAD_ID_2.asEventId())) + + // The other thread is opened + openThreadLambda.assertions() + .isCalledOnce() + .with( + value(A_THREAD_ID_2), + value(AN_EVENT_ID), + ) + } + } + + @Test + fun `present - focus on event in a the room while in a thread of that room opens the room`() = runTest { + val detachedTimeline = FakeTimeline( + mode = Timeline.Mode.FocusedOnEvent(AN_EVENT_ID_2), + timelineItems = flowOf( + listOf( + MatrixTimelineItem.Event( + uniqueId = A_UNIQUE_ID, + event = anEventTimelineItem(), + ) + ) + ) + ) + val liveTimeline = FakeTimeline( + mode = Timeline.Mode.Thread(A_THREAD_ID), + timelineItems = flowOf(emptyList()) + ) + val room = FakeJoinedRoom( + liveTimeline = liveTimeline, + createTimelineResult = { Result.success(detachedTimeline) }, + baseRoom = FakeBaseRoom( + canUserSendMessageResult = { _, _ -> Result.success(true) }, + // The event is in the main timeline, not in a thread + threadRootIdForEventResult = { _ -> Result.success(null) }, + ), + ) + val openRoomLambda = lambdaRecorder { _: RoomId, _: EventId?, _: List -> } + val navigator = FakeMessagesNavigator(onNavigateToRoomLambda = openRoomLambda) + val presenter = createTimelinePresenter( + room = room, + timeline = liveTimeline, + messagesNavigator = navigator, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitFirstItem() + initialState.eventSink.invoke(TimelineEvents.FocusOnEvent(AN_EVENT_ID)) + + awaitItem().also { state -> + assertThat(state.focusedEventId).isEqualTo(AN_EVENT_ID) + assertThat(state.focusRequestState).isEqualTo(FocusRequestState.Requested(AN_EVENT_ID, Duration.ZERO)) + } + + advanceUntilIdle() + + assertThat(awaitItem().focusRequestState).isEqualTo(FocusRequestState.Loading(AN_EVENT_ID)) + + // The focus state will reset + assertThat(awaitItem().focusRequestState).isEqualTo(FocusRequestState.None) + + // The room is opened again + openRoomLambda.assertions() + .isCalledOnce() + .with( + value(room.roomId), + value(AN_EVENT_ID), + value(emptyList()) + ) + } + } + @Test fun `present - show shield hide shield`() = runTest { val presenter = createTimelinePresenter() @@ -754,7 +1004,7 @@ class TimelinePresenterTest { canUserSendMessageResult = { _, _ -> Result.success(true) }, ), ) - val onNavigateToRoomLambda = lambdaRecorder, Unit> { _, _ -> } + val onNavigateToRoomLambda = lambdaRecorder, Unit> { _, _, _ -> } val navigator = FakeMessagesNavigator( onNavigateToRoomLambda = onNavigateToRoomLambda ) @@ -766,6 +1016,8 @@ class TimelinePresenterTest { .isCalledOnce() .with( value(A_ROOM_ID), + // No event id when navigating to a successor/predecessor room + value(null), value(emptyList()) ) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt index 73969a2860..451f5bddcc 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt @@ -36,7 +36,7 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.setSafeContent import io.element.android.wysiwyg.link.Link import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import org.junit.Rule import org.junit.Test import org.junit.rules.TestRule @@ -148,7 +148,7 @@ class TimelineViewTest { eventId = EventId("\$event_$it"), content = aTimelineItemUnknownContent(), ) - }.toPersistentList() + }.toImmutableList() rule.setTimelineView( state = aTimelineState( diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenterTest.kt index 034d5aa3a8..e34bbdcbef 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenterTest.kt @@ -23,7 +23,10 @@ class CustomReactionPresenterTest { @get:Rule val warmUpRule = WarmUpRule() - private val presenter = CustomReactionPresenter(emojibaseProvider = FakeEmojibaseProvider()) + private val presenter = CustomReactionPresenter( + emojibaseProvider = FakeEmojibaseProvider(), + getRecentEmojis = { Result.success(emptyList()) }, + ) @Test fun `present - handle selecting and de-selecting an event`() = runTest { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerPresenterTest.kt new file mode 100644 index 0000000000..cf4930b389 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerPresenterTest.kt @@ -0,0 +1,125 @@ +/* + * Copyright 2025 New Vector 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.timeline.components.customreaction.picker + +import androidx.compose.runtime.InternalComposeApi +import androidx.compose.runtime.currentComposer +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.TurbineTestContext +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.emojibasebindings.Emoji +import io.element.android.emojibasebindings.EmojibaseCategory +import io.element.android.emojibasebindings.EmojibaseStore +import io.element.android.libraries.designsystem.theme.components.SearchBarResultState +import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableMap +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class EmojiPickerPresenterTest { + @get:Rule + val warmUpRule = WarmUpRule() + + @Test + fun `UpdateSearchQuery loads new results`() = runTest { + testPresenter { + skipItems(1) + + val initialState = awaitItem() + assertThat(initialState.searchQuery).isEmpty() + assertThat(initialState.searchResults).isInstanceOf(SearchBarResultState.Initial::class.java) + + initialState.eventSink(EmojiPickerEvents.UpdateSearchQuery("smile")) + assertThat(awaitItem().searchQuery).isEqualTo("smile") + + val stateWithResults = awaitItem() + assertThat(stateWithResults.searchQuery).isEqualTo("smile") + assertThat(stateWithResults.searchResults).isInstanceOf(SearchBarResultState.Results::class.java) + } + } + + @Test + fun `ToggleSearchActive toggles the search state`() = runTest { + testPresenter { + skipItems(1) + + val initialState = awaitItem() + assertThat(initialState.isSearchActive).isFalse() + + initialState.eventSink(EmojiPickerEvents.ToggleSearchActive(true)) + assertThat(awaitItem().isSearchActive).isTrue() + + initialState.eventSink(EmojiPickerEvents.ToggleSearchActive(false)) + assertThat(awaitItem().isSearchActive).isFalse() + } + } + + @Test + fun `recent emojis are automatically added to the categories if present`() = runTest { + val providedCategories = persistentListOf(emojiCategory(EmojibaseCategory.Activity)) + val presenter = createPresenter( + categories = providedCategories, + recentEmojis = persistentListOf("😊"), + ) + testPresenter(presenter) { + skipItems(1) + + val initialState = awaitItem() + assertThat(providedCategories.size).isNotEqualTo(initialState.categories.size) + assertThat(initialState.categories.size).isEqualTo(2) + } + } + + private fun TestScope.createPresenter( + categories: ImmutableList>> = persistentListOf(emojiCategory()), + recentEmojis: ImmutableList = persistentListOf(), + ) = EmojiPickerPresenter( + emojibaseStore = EmojibaseStore(categories.toMap().toImmutableMap()), + recentEmojis = recentEmojis, + coroutineDispatchers = testCoroutineDispatchers(), + ) + + private fun emojiCategory( + category: EmojibaseCategory = EmojibaseCategory.Activity, + emojis: ImmutableList = persistentListOf( + Emoji("1F3C3", "Smile", persistentListOf("smile"), persistentListOf("smile"), "😊", skins = null) + ) + ) = category to emojis + + @OptIn(InternalComposeApi::class) + private suspend fun TestScope.testPresenter( + presenter: EmojiPickerPresenter = createPresenter(), + testBlock: suspend TurbineTestContext.() -> Unit, + ) { + moleculeFlow(RecompositionMode.Immediate) { + // These are needed to load the history icon in the presenter + currentComposer.startProviders(arrayOf( + LocalContext provides InstrumentationRegistry.getInstrumentation().context, + LocalConfiguration provides InstrumentationRegistry.getInstrumentation().context.resources.configuration, + )) + val state = presenter.present() + currentComposer.endProviders() + state + }.test { + testBlock() + } + } +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt index 6040032ebc..0d6a96d75d 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt @@ -46,7 +46,6 @@ import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaRecorder import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceUntilIdle @@ -85,7 +84,7 @@ class VoiceMessageComposerPresenterTest { companion object { private val RECORDING_DURATION = 1.seconds - private val RECORDING_STATE = VoiceMessageState.Recording(RECORDING_DURATION, listOf(0.1f, 0.2f).toPersistentList()) + private val RECORDING_STATE = VoiceMessageState.Recording(RECORDING_DURATION, listOf(0.1f, 0.2f).toImmutableList()) } @Test diff --git a/features/migration/impl/build.gradle.kts b/features/migration/impl/build.gradle.kts index dd445624b9..8926f258bd 100644 --- a/features/migration/impl/build.gradle.kts +++ b/features/migration/impl/build.gradle.kts @@ -19,6 +19,7 @@ android { setupDependencyInjection() dependencies { + implementation(projects.features.announcement.api) implementation(projects.features.migration.api) implementation(projects.libraries.architecture) implementation(projects.libraries.androidutils) @@ -34,5 +35,6 @@ dependencies { testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.sessionStorage.test) testImplementation(projects.libraries.preferences.test) + testImplementation(projects.features.announcement.test) testImplementation(projects.features.rageshake.test) } diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationPresenter.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationPresenter.kt index 7edbc5389f..d4e6d6dc37 100644 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationPresenter.kt +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationPresenter.kt @@ -31,6 +31,7 @@ class MigrationPresenter( ) : Presenter { private val orderedMigrations = migrations.sortedBy { it.order } private val lastMigration: Int = orderedMigrations.lastOrNull()?.order ?: 0 + private var isFreshInstall = false @Composable override fun present(): MigrationState { @@ -49,6 +50,7 @@ class MigrationPresenter( val migrationValue = migrationStoreVersion ?: return@LaunchedEffect if (migrationValue == -1) { Timber.d("Fresh install, or previous installed application did not have the migration mechanism.") + isFreshInstall = true } if (migrationValue == lastMigration) { Timber.d("Current app migration version: $migrationValue. No migration needed.") @@ -59,7 +61,7 @@ class MigrationPresenter( val nextMigration = orderedMigrations.firstOrNull { it.order > migrationValue } if (nextMigration != null) { Timber.d("Current app migration version: $migrationValue. Applying migration: ${nextMigration.order}") - nextMigration.migrate() + nextMigration.migrate(isFreshInstall) migrationStore.setApplicationMigrationVersion(nextMigration.order) } } diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration.kt index a0327e8b09..f14ec89dbe 100644 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration.kt +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration.kt @@ -9,5 +9,5 @@ package io.element.android.features.migration.impl.migrations interface AppMigration { val order: Int - suspend fun migrate() + suspend fun migrate(isFreshInstall: Boolean) } diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration01.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration01.kt index 048a400f6c..88fdb16b9e 100644 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration01.kt +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration01.kt @@ -22,7 +22,7 @@ class AppMigration01( ) : AppMigration { override val order: Int = 1 - override suspend fun migrate() { + override suspend fun migrate(isFreshInstall: Boolean) { logFilesRemover.perform() } } diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration02.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration02.kt index 44f4806c65..0ba2712427 100644 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration02.kt +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration02.kt @@ -27,7 +27,7 @@ class AppMigration02( ) : AppMigration { override val order: Int = 2 - override suspend fun migrate() { + override suspend fun migrate(isFreshInstall: Boolean) { coroutineScope { for (session in sessionStore.getAllSessions()) { val sessionId = SessionId(session.userId) diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration03.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration03.kt index 0cb3573954..e24e18a205 100644 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration03.kt +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration03.kt @@ -21,7 +21,7 @@ class AppMigration03( ) : AppMigration { override val order: Int = 3 - override suspend fun migrate() { - migration01.migrate() + override suspend fun migrate(isFreshInstall: Boolean) { + migration01.migrate(isFreshInstall) } } diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration04.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration04.kt index 8ab4921038..f1f16b7a97 100644 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration04.kt +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration04.kt @@ -27,7 +27,7 @@ class AppMigration04( } override val order: Int = 4 - override suspend fun migrate() { + override suspend fun migrate(isFreshInstall: Boolean) { runCatchingExceptions { context.getDatabasePath(NOTIFICATION_FILE_NAME).delete() } } } diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration05.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration05.kt index 109ff7e0b7..2a60822c5d 100644 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration05.kt +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration05.kt @@ -22,7 +22,7 @@ class AppMigration05( ) : AppMigration { override val order: Int = 5 - override suspend fun migrate() { + override suspend fun migrate(isFreshInstall: Boolean) { val allSessions = sessionStore.getAllSessions() for (session in allSessions) { if (session.sessionPath.isEmpty()) { diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration06.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration06.kt index 2eb98b9e5f..be3050f919 100644 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration06.kt +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration06.kt @@ -25,7 +25,7 @@ class AppMigration06( ) : AppMigration { override val order: Int = 6 - override suspend fun migrate() { + override suspend fun migrate(isFreshInstall: Boolean) { val allSessions = sessionStore.getAllSessions() for (session in allSessions) { if (session.cachePath.isEmpty()) { diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration07.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration07.kt index fe88817796..5367323b75 100644 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration07.kt +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration07.kt @@ -22,7 +22,7 @@ class AppMigration07( ) : AppMigration { override val order: Int = 7 - override suspend fun migrate() { + override suspend fun migrate(isFreshInstall: Boolean) { logFilesRemover.perform { file -> file.name.startsWith("logs-") } diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration08.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration08.kt new file mode 100644 index 0000000000..12355b4bc4 --- /dev/null +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration08.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2025 New Vector 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.migration.impl.migrations + +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesIntoSet +import dev.zacsweers.metro.Inject +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.announcement.api.AnnouncementService + +/** + * Ensure the new notification sound banner is displayed, but only on application upgrade. + */ +@ContributesIntoSet(AppScope::class) +@Inject +class AppMigration08( + private val announcementService: AnnouncementService, +) : AppMigration { + override val order: Int = 8 + + override suspend fun migrate(isFreshInstall: Boolean) { + if (!isFreshInstall) { + announcementService.showAnnouncement(Announcement.NewNotificationSound) + } + } +} diff --git a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/MigrationPresenterTest.kt b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/MigrationPresenterTest.kt index 635a06f028..e5898ea0dd 100644 --- a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/MigrationPresenterTest.kt +++ b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/MigrationPresenterTest.kt @@ -15,8 +15,10 @@ import io.element.android.features.migration.impl.migrations.AppMigration import io.element.android.libraries.architecture.AsyncData import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.consumeItemsUntilPredicate -import io.element.android.tests.testutils.lambda.LambdaNoParamRecorder +import io.element.android.tests.testutils.lambda.LambdaOneParamRecorder +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 kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -48,13 +50,18 @@ class MigrationPresenterTest { assertThat(store.applicationMigrationVersion().first()).isEqualTo(migrations.maxOf { it.order }) } for (migration in migrations) { - migration.migrateLambda.assertions().isCalledOnce() + migration.migrateLambda.assertions().isCalledOnce().with(value(true)) } } @Test fun `present - no migration should occurs if ApplicationMigrationVersion is the last one`() = runTest { - val migrations = (1..10).map { FakeAppMigration(it) } + val migrations = (1..10).map { + FakeAppMigration( + order = it, + migrateLambda = lambdaRecorder { lambdaError() }, + ) + } val store = InMemoryMigrationStore(migrations.maxOf { it.order }) val presenter = createPresenter( migrationStore = store, @@ -90,7 +97,7 @@ class MigrationPresenterTest { consumeItemsUntilPredicate { it.migrationAction is AsyncData.Success } assertThat(store.applicationMigrationVersion().first()).isEqualTo(migrations.maxOf { it.order }) for (migration in migrations) { - migration.migrateLambda.assertions().isCalledOnce() + migration.migrateLambda.assertions().isCalledOnce().with(value(false)) } } } @@ -106,9 +113,9 @@ private fun createPresenter( private class FakeAppMigration( override val order: Int, - val migrateLambda: LambdaNoParamRecorder = lambdaRecorder { -> }, + val migrateLambda: LambdaOneParamRecorder = lambdaRecorder { }, ) : AppMigration { - override suspend fun migrate() { - migrateLambda() + override suspend fun migrate(isFreshInstall: Boolean) { + migrateLambda(isFreshInstall) } } diff --git a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration01Test.kt b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration01Test.kt index 580be7d705..6e1b663e3c 100644 --- a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration01Test.kt +++ b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration01Test.kt @@ -17,7 +17,7 @@ class AppMigration01Test { val logsFileRemover = FakeLogFilesRemover() val migration = AppMigration01(logsFileRemover) - migration.migrate() + migration.migrate(true) logsFileRemover.performLambda.assertions().isCalledOnce() } diff --git a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration02Test.kt b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration02Test.kt index fb7dbf5281..08c10160b8 100644 --- a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration02Test.kt +++ b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration02Test.kt @@ -30,7 +30,7 @@ class AppMigration02Test { ) val migration = AppMigration02(sessionStore = sessionStore, sessionPreferenceStoreFactory = sessionPreferencesStoreFactory) - migration.migrate() + migration.migrate(true) // We got the session preferences store sessionPreferencesStoreFactory.getLambda.assertions().isCalledOnce() diff --git a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration03Test.kt b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration03Test.kt index e4911faaa5..0f4db78aa2 100644 --- a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration03Test.kt +++ b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration03Test.kt @@ -17,7 +17,7 @@ class AppMigration03Test { val logsFileRemover = FakeLogFilesRemover() val migration = AppMigration03(migration01 = AppMigration01(logsFileRemover)) - migration.migrate() + migration.migrate(true) logsFileRemover.performLambda.assertions().isCalledOnce() } diff --git a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration04Test.kt b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration04Test.kt index f272dadc22..68cbfde689 100644 --- a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration04Test.kt +++ b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration04Test.kt @@ -28,7 +28,7 @@ class AppMigration04Test { val migration = AppMigration04(context) - migration.migrate() + migration.migrate(true) // Check that the file has been deleted assertThat(file.exists()).isFalse() diff --git a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration05Test.kt b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration05Test.kt index af71905635..ff5b8cd40a 100644 --- a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration05Test.kt +++ b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration05Test.kt @@ -27,7 +27,7 @@ class AppMigration05Test { ) ) val migration = AppMigration05(sessionStore = sessionStore, baseDirectory = File("/a/path")) - migration.migrate() + migration.migrate(true) val storedData = sessionStore.getSession(A_SESSION_ID.value)!! assertThat(storedData.sessionPath).isEqualTo("/a/path/${A_SESSION_ID.value.replace(':', '_')}") } @@ -43,7 +43,7 @@ class AppMigration05Test { ) ) val migration = AppMigration05(sessionStore = sessionStore, baseDirectory = File("/a/path")) - migration.migrate() + migration.migrate(true) val storedData = sessionStore.getSession(A_SESSION_ID.value)!! assertThat(storedData.sessionPath).isEqualTo("/a/path/existing") } diff --git a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration06Test.kt b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration06Test.kt index 095085cd17..e7e15ba821 100644 --- a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration06Test.kt +++ b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration06Test.kt @@ -28,7 +28,7 @@ class AppMigration06Test { ) ) val migration = AppMigration06(sessionStore = sessionStore, cacheDirectory = File("/a/path/cache")) - migration.migrate() + migration.migrate(true) val storedData = sessionStore.getSession(A_SESSION_ID.value)!! assertThat(storedData.cachePath).isEqualTo("/a/path/cache/AN_ID") } @@ -44,7 +44,7 @@ class AppMigration06Test { ) ) val migration = AppMigration05(sessionStore = sessionStore, baseDirectory = File("/a/path/cache")) - migration.migrate() + migration.migrate(true) val storedData = sessionStore.getSession(A_SESSION_ID.value)!! assertThat(storedData.cachePath).isEqualTo("/a/path/existing") } diff --git a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration07Test.kt b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration07Test.kt index a375ef8563..a2575b32df 100644 --- a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration07Test.kt +++ b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration07Test.kt @@ -24,7 +24,7 @@ class AppMigration07Test { } val logsFileRemover = FakeLogFilesRemover(performLambda = performLambda) val migration = AppMigration07(logsFileRemover) - migration.migrate() + migration.migrate(true) performLambda.assertions().isCalledOnce() } } diff --git a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration08Test.kt b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration08Test.kt new file mode 100644 index 0000000000..75ffa28882 --- /dev/null +++ b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration08Test.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2024 New Vector 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.migration.impl.migrations + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.rageshake.test.logs.FakeAnnouncementService +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 kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class AppMigration08Test { + @Test + fun `migration on fresh install should not invoke the AnnouncementService`() = runTest { + val service = FakeAnnouncementService( + showAnnouncementResult = { lambdaError() }, + ) + val migration = AppMigration08(service) + migration.migrate(isFreshInstall = true) + assertThat(service.announcementsToShowFlow().first()).isEmpty() + } + + @Test + fun `migration on upgrade should invoke the AnnouncementService`() = runTest { + val showAnnouncementResult = lambdaRecorder { } + val service = FakeAnnouncementService( + showAnnouncementResult = showAnnouncementResult, + ) + val migration = AppMigration08(service) + migration.migrate(isFreshInstall = false) + showAnnouncementResult.assertions().isCalledOnce() + .with(value(Announcement.NewNotificationSound)) + } +} diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollNode.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollNode.kt index 943ef5bd38..992fde1c4e 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollNode.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollNode.kt @@ -14,7 +14,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode import io.element.android.features.poll.api.create.CreatePollMode @@ -26,7 +26,7 @@ import io.element.android.services.analytics.api.AnalyticsService import java.util.concurrent.atomic.AtomicBoolean @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class CreatePollNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenter.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenter.kt index 2d760d8314..5136571acd 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenter.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenter.kt @@ -18,7 +18,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.Composer import im.vector.app.features.analytics.plan.PollCreation import io.element.android.features.messages.api.MessageComposerContext @@ -33,11 +33,10 @@ import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.services.analytics.api.AnalyticsService import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.launch import timber.log.Timber -@Inject +@AssistedInject class CreatePollPresenter( repositoryFactory: PollRepository.Factory, private val analyticsService: AnalyticsService, @@ -79,7 +78,7 @@ class CreatePollPresenter( repository.getPoll(mode.eventId).onSuccess { val loadedPoll = PollFormState( question = it.question, - answers = it.answers.map(PollAnswer::text).toPersistentList(), + answers = it.answers.map(PollAnswer::text).toImmutableList(), isDisclosed = it.kind.isDisclosed, ) initialPoll = loadedPoll diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStateProvider.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStateProvider.kt index dd773c97fb..f1f345b468 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStateProvider.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStateProvider.kt @@ -9,8 +9,7 @@ package io.element.android.features.poll.impl.create import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.matrix.api.poll.PollKind -import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList class CreatePollStateProvider : PreviewParameterProvider { override val values: Sequence @@ -20,7 +19,7 @@ class CreatePollStateProvider : PreviewParameterProvider { canCreate = false, canAddAnswer = true, question = "", - answers = persistentListOf( + answers = listOf( Answer("", false), Answer("", false) ), @@ -33,7 +32,7 @@ class CreatePollStateProvider : PreviewParameterProvider { canCreate = true, canAddAnswer = true, question = "What type of food should we have?", - answers = persistentListOf( + answers = listOf( Answer("Italian \uD83C\uDDEE\uD83C\uDDF9", false), Answer("Chinese \uD83C\uDDE8\uD83C\uDDF3", false), ), @@ -46,7 +45,7 @@ class CreatePollStateProvider : PreviewParameterProvider { canCreate = true, canAddAnswer = true, question = "What type of food should we have?", - answers = persistentListOf( + answers = listOf( Answer("Italian \uD83C\uDDEE\uD83C\uDDF9", false), Answer("Chinese \uD83C\uDDE8\uD83C\uDDF3", false), ), @@ -59,7 +58,7 @@ class CreatePollStateProvider : PreviewParameterProvider { canCreate = true, canAddAnswer = true, question = "What type of food should we have?", - answers = persistentListOf( + answers = listOf( Answer("Italian \uD83C\uDDEE\uD83C\uDDF9", true), Answer("Chinese \uD83C\uDDE8\uD83C\uDDF3", true), Answer("Brazilian \uD83C\uDDE7\uD83C\uDDF7", true), @@ -74,7 +73,7 @@ class CreatePollStateProvider : PreviewParameterProvider { canCreate = true, canAddAnswer = false, question = "Should there be more than 20 answers?", - answers = persistentListOf( + answers = listOf( Answer("1", true), Answer("2", true), Answer("3", true), @@ -108,7 +107,7 @@ class CreatePollStateProvider : PreviewParameterProvider { " Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor" + " in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt" + " in culpa qui officia deserunt mollit anim id est laborum.", - answers = persistentListOf( + answers = listOf( Answer( "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + " Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis a.", @@ -129,7 +128,7 @@ class CreatePollStateProvider : PreviewParameterProvider { canCreate = false, canAddAnswer = true, question = "", - answers = persistentListOf( + answers = listOf( Answer("", false), Answer("", false) ), @@ -142,7 +141,7 @@ class CreatePollStateProvider : PreviewParameterProvider { canCreate = false, canAddAnswer = true, question = "", - answers = persistentListOf( + answers = listOf( Answer("", false), Answer("", false) ), @@ -158,7 +157,7 @@ private fun aCreatePollState( canCreate: Boolean, canAddAnswer: Boolean, question: String, - answers: PersistentList, + answers: List, showBackConfirmation: Boolean, showDeleteConfirmation: Boolean, pollKind: PollKind @@ -168,7 +167,7 @@ private fun aCreatePollState( canSave = canCreate, canAddAnswer = canAddAnswer, question = question, - answers = answers, + answers = answers.toImmutableList(), showBackConfirmation = showBackConfirmation, showDeleteConfirmation = showDeleteConfirmation, pollKind = pollKind, diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/PollFormState.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/PollFormState.kt index 6ac7797205..34b9d6206b 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/PollFormState.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/PollFormState.kt @@ -13,7 +13,7 @@ import io.element.android.features.poll.impl.PollConstants import io.element.android.features.poll.impl.PollConstants.MIN_ANSWERS import io.element.android.libraries.matrix.api.poll.PollKind import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList /** * Represents the state of the poll creation / edit form. @@ -28,7 +28,7 @@ data class PollFormState( companion object { val Empty = PollFormState( question = "", - answers = MutableList(MIN_ANSWERS) { "" }.toPersistentList(), + answers = MutableList(MIN_ANSWERS) { "" }.toImmutableList(), isDisclosed = true, ) } @@ -49,7 +49,7 @@ data class PollFormState( return this } - return copy(answers = (answers + "").toPersistentList()) + return copy(answers = (answers + "").toImmutableList()) } /** @@ -66,7 +66,7 @@ data class PollFormState( return this } - return copy(answers = answers.filterIndexed { i, _ -> i != index }.toPersistentList()) + return copy(answers = answers.filterIndexed { i, _ -> i != index }.toImmutableList()) } /** @@ -82,7 +82,7 @@ data class PollFormState( fun withAnswerChanged(index: Int, rawAnswer: String): PollFormState = copy(answers = answers.toMutableList().apply { this[index] = rawAnswer.take(PollConstants.MAX_ANSWER_LENGTH) - }.toPersistentList()) + }.toImmutableList()) /** * Whether a new answer can be added. @@ -114,7 +114,7 @@ internal val pollFormStateSaver = mapSaver( restore = { saved -> PollFormState( question = saved["question"] as String, - answers = (saved["answers"] as Array<*>).map { it as String }.toPersistentList(), + answers = (saved["answers"] as Array<*>).map { it as String }.toImmutableList(), isDisclosed = saved["isDisclosed"] as Boolean, ) } diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/data/PollRepository.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/data/PollRepository.kt index 983e3157a3..ee53ca7826 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/data/PollRepository.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/data/PollRepository.kt @@ -9,7 +9,7 @@ package io.element.android.features.poll.impl.data import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.core.extensions.flatMap import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.matrix.api.core.EventId @@ -26,7 +26,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.first -@Inject +@AssistedInject class PollRepository( private val room: JoinedRoom, private val defaultTimelineProvider: TimelineProvider, diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryFlowNode.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryFlowNode.kt index f1710712c0..19142508a1 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryFlowNode.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryFlowNode.kt @@ -16,7 +16,7 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.poll.api.create.CreatePollEntryPoint import io.element.android.features.poll.api.create.CreatePollMode @@ -29,7 +29,7 @@ import io.element.android.libraries.matrix.api.timeline.Timeline import kotlinx.parcelize.Parcelize @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class PollHistoryFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryNode.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryNode.kt index 2b6f79fcf4..3fdfdb921f 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryNode.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryNode.kt @@ -14,13 +14,13 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class PollHistoryNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStateProvider.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStateProvider.kt index e8161d9fe1..2d126ce7cf 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStateProvider.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryStateProvider.kt @@ -13,7 +13,7 @@ import io.element.android.features.poll.api.pollcontent.aPollContentState import io.element.android.features.poll.impl.history.model.PollHistoryFilter import io.element.android.features.poll.impl.history.model.PollHistoryItem import io.element.android.features.poll.impl.history.model.PollHistoryItems -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList class PollHistoryStateProvider : PreviewParameterProvider { override val values: Sequence @@ -53,8 +53,8 @@ internal fun aPollHistoryState( hasMoreToLoad = hasMoreToLoad, activeFilter = activeFilter, pollHistoryItems = PollHistoryItems( - ongoing = currentItems.toPersistentList(), - past = currentItems.toPersistentList(), + ongoing = currentItems.toImmutableList(), + past = currentItems.toImmutableList(), ), eventSink = eventSink, ) diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/model/PollHistoryItemsFactory.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/model/PollHistoryItemsFactory.kt index 2a9c802e6b..c895a2c16c 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/model/PollHistoryItemsFactory.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/model/PollHistoryItemsFactory.kt @@ -14,7 +14,7 @@ import io.element.android.libraries.dateformatter.api.DateFormatter import io.element.android.libraries.dateformatter.api.DateFormatterMode import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.PollContent -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.withContext @Inject @@ -36,8 +36,8 @@ class PollHistoryItemsFactory( } } PollHistoryItems( - ongoing = ongoing.toPersistentList(), - past = past.toPersistentList() + ongoing = ongoing.toImmutableList(), + past = past.toImmutableList() ) } diff --git a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/PollFormStateSaverTest.kt b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/PollFormStateSaverTest.kt index b400bef2e6..bab6171477 100644 --- a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/PollFormStateSaverTest.kt +++ b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/PollFormStateSaverTest.kt @@ -9,7 +9,7 @@ package io.element.android.features.poll.impl.create import androidx.compose.runtime.saveable.SaverScope import com.google.common.truth.Truth.assertThat -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.persistentListOf import org.junit.Test class PollFormStateSaverTest { @@ -21,7 +21,7 @@ class PollFormStateSaverTest { fun `test save and restore`() { val state = PollFormState( question = "question", - answers = listOf("answer1", "answer2").toPersistentList(), + answers = persistentListOf("answer1", "answer2"), isDisclosed = true, ) diff --git a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/PollFormStateTest.kt b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/PollFormStateTest.kt index f158e9c803..9594903c69 100644 --- a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/PollFormStateTest.kt +++ b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/PollFormStateTest.kt @@ -10,7 +10,7 @@ package io.element.android.features.poll.impl.create import com.google.common.truth.Truth.assertThat import io.element.android.features.poll.impl.PollConstants import io.element.android.libraries.matrix.api.poll.PollKind -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import org.junit.Test class PollFormStateTest { @@ -47,7 +47,7 @@ class PollFormStateTest { val state = PollFormState.Empty val newState = state.withAnswerChanged(1, "New answer") assertThat(newState).isEqualTo(PollFormState.Empty.copy( - answers = listOf("", "New answer").toPersistentList() + answers = listOf("", "New answer").toImmutableList() )) } @@ -58,7 +58,7 @@ class PollFormStateTest { val state = PollFormState.Empty val newState = state.withAnswerChanged(1, tooLongAnswer) assertThat(newState).isEqualTo(PollFormState.Empty.copy( - answers = listOf("", truncatedAnswer).toPersistentList() + answers = listOf("", truncatedAnswer).toImmutableList() )) } @@ -101,7 +101,7 @@ class PollFormStateTest { @Test fun `is valid is false when not enough answers`() { - val state = aValidPollFormState().copy(answers = listOf("").toPersistentList()) + val state = aValidPollFormState().copy(answers = listOf("").toImmutableList()) assertThat(state.isValid).isFalse() } @@ -127,10 +127,10 @@ class PollFormStateTest { private fun aValidPollFormState(): PollFormState { return PollFormState.Empty.copy( question = "question", - answers = listOf("answer1", "answer2").toPersistentList(), + answers = listOf("answer1", "answer2").toImmutableList(), isDisclosed = true, ) } private fun PollFormState.withBlankAnswers(numAnswers: Int): PollFormState = - copy(answers = List(numAnswers) { "" }.toPersistentList()) + copy(answers = List(numAnswers) { "" }.toImmutableList()) diff --git a/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/PreferencesEntryPoint.kt b/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/PreferencesEntryPoint.kt index f41d497b18..c0affde2df 100644 --- a/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/PreferencesEntryPoint.kt +++ b/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/PreferencesEntryPoint.kt @@ -15,7 +15,6 @@ import io.element.android.libraries.architecture.FeatureEntryPoint import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.core.SessionId import kotlinx.parcelize.Parcelize interface PreferencesEntryPoint : FeatureEntryPoint { @@ -41,9 +40,10 @@ interface PreferencesEntryPoint : FeatureEntryPoint { } interface Callback : Plugin { + fun onAddAccount() fun onOpenBugReport() fun onSecureBackupClick() fun onOpenRoomNotificationSettings(roomId: RoomId) - fun navigateTo(sessionId: SessionId, roomId: RoomId, eventId: EventId) + fun navigateTo(roomId: RoomId, eventId: EventId) } } diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts index d3edba5833..eb057a9d53 100644 --- a/features/preferences/impl/build.gradle.kts +++ b/features/preferences/impl/build.gradle.kts @@ -105,6 +105,7 @@ dependencies { testImplementation(projects.features.logout.test) testImplementation(projects.libraries.indicator.test) testImplementation(projects.libraries.pushproviders.test) + testImplementation(projects.libraries.sessionStorage.test) testImplementation(projects.services.analytics.test) testImplementation(projects.services.toolbox.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 49e297af08..e4ba87c43a 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 @@ -18,7 +18,7 @@ import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.deactivation.api.AccountDeactivationEntryPoint import io.element.android.features.licenses.api.OpenSourceLicensesEntryPoint @@ -30,6 +30,7 @@ import io.element.android.features.preferences.impl.advanced.AdvancedSettingsNod import io.element.android.features.preferences.impl.analytics.AnalyticsSettingsNode import io.element.android.features.preferences.impl.blockedusers.BlockedUsersNode import io.element.android.features.preferences.impl.developer.DeveloperSettingsNode +import io.element.android.features.preferences.impl.labs.LabsNode import io.element.android.features.preferences.impl.notifications.NotificationSettingsNode import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingNode import io.element.android.features.preferences.impl.root.PreferencesRootNode @@ -41,14 +42,13 @@ import io.element.android.libraries.architecture.createNode 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.core.SessionId import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint import io.element.android.libraries.troubleshoot.api.PushHistoryEntryPoint import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class PreferencesFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, @@ -76,6 +76,9 @@ class PreferencesFlowNode( @Parcelize data object AdvancedSettings : NavTarget + @Parcelize + data object Labs : NavTarget + @Parcelize data object AnalyticsSettings : NavTarget @@ -117,6 +120,10 @@ class PreferencesFlowNode( return when (navTarget) { NavTarget.Root -> { val callback = object : PreferencesRootNode.Callback { + override fun onAddAccount() { + plugins().forEach { it.onAddAccount() } + } + override fun onOpenBugReport() { plugins().forEach { it.onOpenBugReport() } } @@ -149,6 +156,10 @@ class PreferencesFlowNode( backstack.push(NavTarget.AdvancedSettings) } + override fun onOpenLabs() { + backstack.push(NavTarget.Labs) + } + override fun onOpenUserProfile(matrixUser: MatrixUser) { backstack.push(NavTarget.UserProfile(matrixUser)) } @@ -175,6 +186,9 @@ class PreferencesFlowNode( } createNode(buildContext, listOf(developerSettingsCallback)) } + NavTarget.Labs -> { + createNode(buildContext) + } NavTarget.About -> { val callback = object : AboutNode.Callback { override fun openOssLicenses() { @@ -226,8 +240,8 @@ class PreferencesFlowNode( } } - override fun onItemClick(sessionId: SessionId, roomId: RoomId, eventId: EventId) { - plugins().forEach { it.navigateTo(sessionId, roomId, eventId) } + override fun navigateTo(roomId: RoomId, eventId: EventId) { + plugins().forEach { it.navigateTo(roomId, eventId) } } }) .build() diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutNode.kt index bedd778ee7..37de32bab7 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutNode.kt @@ -15,14 +15,14 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class AboutNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsNode.kt index d4a5cbea37..adcf12cf42 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsNode.kt @@ -13,12 +13,12 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class AdvancedSettingsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 6f34d216f6..e1c6ce259a 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 @@ -45,7 +45,7 @@ import io.element.android.libraries.preferences.api.store.VideoCompressionPreset import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.compose.LocalAnalyticsService import io.element.android.services.analyticsproviders.api.trackers.captureInteraction -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList @Composable fun AdvancedSettingsView( @@ -73,7 +73,7 @@ fun AdvancedSettingsView( PreferenceDropdown( title = stringResource(id = CommonStrings.common_appearance), selectedOption = state.theme, - options = ThemeOption.entries.toPersistentList(), + options = ThemeOption.entries.toImmutableList(), onSelectOption = { themeOption -> state.eventSink(AdvancedSettingsEvents.SetTheme(themeOption)) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsNode.kt index 07b32b8d07..7f96e8f181 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsNode.kt @@ -13,12 +13,12 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class AnalyticsSettingsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersNode.kt index 89a4a6c571..4c94a8dfb4 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersNode.kt @@ -13,12 +13,12 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class BlockedUsersNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 8a61bd4bed..5fbbe3e797 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 @@ -25,7 +25,7 @@ import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -83,7 +83,7 @@ class BlockedUsersPresenter( } } return BlockedUsersState( - blockedUsers = ignoredMatrixUser.toPersistentList(), + blockedUsers = ignoredMatrixUser.toImmutableList(), unblockUserAction = unblockUserAction.value, eventSink = ::handleEvents ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStateProvider.kt index ba90f93ba5..ac344ba838 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStateProvider.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUserList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList class BlockedUsersStateProvider : PreviewParameterProvider { override val values: Sequence @@ -32,7 +32,7 @@ internal fun aBlockedUsersState( eventSink: (BlockedUsersEvents) -> Unit = {}, ): BlockedUsersState { return BlockedUsersState( - blockedUsers = blockedUsers.toPersistentList(), + blockedUsers = blockedUsers.toImmutableList(), unblockUserAction = unblockUserAction, eventSink = eventSink, ) 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 b1761e54c0..6208d0123e 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 @@ -16,13 +16,13 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.designsystem.showkase.getBrowserIntent import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class DeveloperSettingsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 e07c30bac9..fe7a3461b8 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 @@ -37,11 +37,9 @@ import io.element.android.libraries.featureflag.api.Feature import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.ui.model.FeatureUiModel -import io.element.android.libraries.matrix.api.tracing.TraceLogPack import io.element.android.libraries.preferences.api.store.AppPreferencesStore import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.map @@ -82,16 +80,16 @@ class DeveloperSettingsPresenter( appPreferencesStore.getTracingLogLevelFlow().map { AsyncData.Success(it.toLogLevelItem()) } } val tracingLogLevel by tracingLogLevelFlow.collectAsState(initial = AsyncData.Uninitialized) - val tracingLogPacks by produceState(persistentListOf()) { + val tracingLogPacks by produceState(persistentListOf()) { appPreferencesStore.getTracingLogPacksFlow() // Sort the entries alphabetically by its title - .map { it.sortedBy { it.title }.toPersistentList() } - .collectLatest { value = it } + .map { it.sortedBy { it.title } } + .collectLatest { value = it.toImmutableList() } } LaunchedEffect(Unit) { - FeatureFlags.entries - .filter { it.isFinished.not() } + featureFlagService.getAvailableFeatures() + .filter { it.isInLabs.not() && it.isFinished.not() } .run { // Never display room directory search in release builds for Play Store if (buildMeta.flavorDescription == "GooglePlay" && buildMeta.buildType == BuildType.RELEASE) { @@ -169,6 +167,7 @@ class DeveloperSettingsPresenter( key = feature.key, title = feature.title, description = feature.description, + icon = null, isEnabled = isEnabled ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStateProvider.kt index 1585a3b8dd..6ccd857552 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStateProvider.kt @@ -14,7 +14,7 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.featureflag.ui.model.aFeatureUiModelList import io.element.android.libraries.matrix.api.tracing.TraceLogPack -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList open class DeveloperSettingsStateProvider : PreviewParameterProvider { override val values: Sequence @@ -43,7 +43,7 @@ fun aDeveloperSettingsState( clearCacheAction = clearCacheAction, customElementCallBaseUrlState = customElementCallBaseUrlState, tracingLogLevel = AsyncData.Success(LogLevelItem.INFO), - tracingLogPacks = traceLogPacks.toPersistentList(), + tracingLogPacks = traceLogPacks.toImmutableList(), eventSink = eventSink, ) 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 e00b178dee..97f08f7a16 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 @@ -36,7 +36,7 @@ import io.element.android.libraries.featureflag.ui.FeatureListView import io.element.android.libraries.featureflag.ui.model.FeatureUiModel import io.element.android.libraries.matrix.api.tracing.TraceLogPack import io.element.android.libraries.ui.strings.CommonStrings -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList @Composable fun DeveloperSettingsView( @@ -66,7 +66,7 @@ fun DeveloperSettingsView( title = "Tracing log level", supportingText = "Requires app reboot", selectedOption = state.tracingLogLevel.dataOrNull(), - options = LogLevelItem.entries.toPersistentList(), + options = LogLevelItem.entries.toImmutableList(), onSelectOption = { logLevel -> state.eventSink(DeveloperSettingsEvents.SetTracingLogLevel(logLevel)) } 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/LabsEvents.kt new file mode 100644 index 0000000000..0f652a5c5c --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsEvents.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2025 New Vector 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.labs + +import io.element.android.libraries.featureflag.ui.model.FeatureUiModel + +sealed interface LabsEvents { + data class ToggleFeature(val feature: FeatureUiModel) : LabsEvents +} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsNode.kt similarity index 53% rename from features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceNode.kt rename to features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsNode.kt index a4828fe9fe..5c2b5ed0e3 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsNode.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.space.impl +package io.element.android.features.preferences.impl.labs import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -13,33 +13,20 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode -import io.element.android.features.space.api.SpaceEntryPoint -import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject -class SpaceNode( +@AssistedInject +class LabsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, - presenterFactory: SpacePresenter.Factory, + private val presenter: LabsPresenter, ) : Node(buildContext, plugins = plugins) { - private val inputs: SpaceEntryPoint.Inputs = inputs() - private val callback = plugins.filterIsInstance().single() - private val presenter = presenterFactory.create(inputs) - @Composable override fun View(modifier: Modifier) { val state = presenter.present() - SpaceView( - state = state, - onBackClick = ::navigateUp, - onRoomClick = { roomId -> - callback.onOpenRoom(roomId) - }, - modifier = modifier - ) + LabsView(state = state, onBack = ::navigateUp) } } 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 new file mode 100644 index 0000000000..5e74454d75 --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsPresenter.kt @@ -0,0 +1,124 @@ +/* + * Copyright 2025 New Vector 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.labs + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.key +import androidx.compose.runtime.mutableStateMapOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshots.SnapshotStateMap +import dev.zacsweers.metro.Inject +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.preferences.impl.R +import io.element.android.features.preferences.impl.tasks.ClearCacheUseCase +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.core.bool.orFalse +import io.element.android.libraries.designsystem.theme.components.IconSource +import io.element.android.libraries.featureflag.api.Feature +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.featureflag.ui.model.FeatureUiModel +import io.element.android.services.toolbox.api.strings.StringProvider +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.launch + +@Inject +class LabsPresenter( + private val stringProvider: StringProvider, + private val featureFlagService: FeatureFlagService, + private val clearCacheUseCase: ClearCacheUseCase, +) : Presenter { + @Composable + override fun present(): LabsState { + val coroutineScope = rememberCoroutineScope() + val features = remember { + val entries = featureFlagService.getAvailableFeatures() + .filter { it.isInLabs && !it.isFinished } + .map { it.key to it } + mutableStateMapOf(*entries.toTypedArray()) + } + val enabledFeatures = remember { + mutableStateMapOf() + } + + LaunchedEffect(Unit) { + for (feature in features.values) { + val isEnabled = featureFlagService.isFeatureEnabled(feature) + enabledFeatures[feature.key] = isEnabled + } + } + + var isApplyingChanges by remember { mutableStateOf(false) } + + val featureUiModels = createUiModels(features, enabledFeatures) + + fun handleEvent(event: LabsEvents) { + when (event) { + is LabsEvents.ToggleFeature -> coroutineScope.launch { + val feature = features[event.feature.key] ?: return@launch + val isEnabled = featureFlagService.isFeatureEnabled(feature) + featureFlagService.setFeatureEnabled(feature = feature, enabled = !isEnabled) + enabledFeatures[feature.key] = !isEnabled + + when (feature.key) { + FeatureFlags.Threads.key -> { + // Threads require a cache clear to recreate the event cache + clearCacheUseCase() + isApplyingChanges = true + } + } + } + } + } + + return LabsState( + features = featureUiModels, + isApplyingChanges = isApplyingChanges, + eventSink = ::handleEvent, + ) + } + + @Composable + private fun createUiModels( + features: SnapshotStateMap, + enabledFeatures: SnapshotStateMap + ): ImmutableList { + return features.values.map { feature -> + key(feature.key) { + val isEnabled = enabledFeatures[feature.key].orFalse() + val title = when (feature) { + FeatureFlags.Threads -> stringProvider.getString(R.string.screen_labs_enable_threads) + else -> feature.title + } + val description = when (feature) { + FeatureFlags.Threads -> stringProvider.getString(R.string.screen_labs_enable_threads_description) + else -> feature.description + } + val icon = when (feature) { + FeatureFlags.Threads -> CompoundIcons.Threads() + else -> null + } + remember(feature, isEnabled) { + FeatureUiModel( + key = feature.key, + title = title, + description = description, + icon = icon?.let(IconSource::Vector), + isEnabled = isEnabled + ) + } + } + }.toImmutableList() + } +} 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 new file mode 100644 index 0000000000..0925cd7893 --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsState.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2025 New Vector 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.labs + +import io.element.android.libraries.featureflag.ui.model.FeatureUiModel +import kotlinx.collections.immutable.ImmutableList + +data class LabsState( + val features: ImmutableList, + val isApplyingChanges: Boolean, + val eventSink: (LabsEvents) -> Unit, +) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsStateProvider.kt new file mode 100644 index 0000000000..df804cd409 --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsStateProvider.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2025 New Vector 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.labs + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.designsystem.icons.CompoundDrawables +import io.element.android.libraries.designsystem.theme.components.IconSource +import io.element.android.libraries.featureflag.ui.model.FeatureUiModel +import kotlinx.collections.immutable.toImmutableList + +internal class LabsStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aLabsState(features = aFeatureList()), + aLabsState(features = aFeatureList(), isApplyingChanges = true), + ) +} + +internal fun aLabsState( + features: List = emptyList(), + isApplyingChanges: Boolean = false, +) = LabsState( + features = features.toImmutableList(), + isApplyingChanges = isApplyingChanges, + eventSink = {}, +) + +internal fun aFeatureList() = listOf( + FeatureUiModel( + key = "feature_1", + title = "Feature 1", + description = "This is a description of feature 1.", + isEnabled = true, + icon = IconSource.Resource(CompoundDrawables.ic_compound_threads), + ), + FeatureUiModel( + key = "feature_2", + title = "Feature 2", + description = "This is a description of feature 2.", + isEnabled = false, + icon = IconSource.Resource(CompoundDrawables.ic_compound_video_call), + ) +) 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 new file mode 100644 index 0000000000..2738da4b63 --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/labs/LabsView.kt @@ -0,0 +1,107 @@ +/* + * Copyright 2025 New Vector 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.labs + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.preferences.impl.R +import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule +import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage +import io.element.android.libraries.designsystem.components.BigIcon +import io.element.android.libraries.designsystem.components.ProgressDialog +import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.components.list.ListItemContent +import io.element.android.libraries.designsystem.components.list.SwitchListItem +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.TopAppBar + +/** + * The contents of the Labs screen. + * Design: https://www.figma.com/design/V0dkfRAW6T3yCQKjahpzkX/ER-46-EX--Threads?node-id=2004-27319&t=yssy1yYYigsGON3s-0 + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun LabsView( + state: LabsState, + onBack: () -> Unit, + modifier: Modifier = Modifier, +) { + if (state.isApplyingChanges) { + ProgressDialog() + } + + BackHandler( + enabled = !state.isApplyingChanges, + onBack = onBack, + ) + + HeaderFooterPage( + modifier = modifier + .fillMaxSize() + .systemBarsPadding() + .imePadding(), + topBar = { + TopAppBar( + titleStr = stringResource(R.string.screen_labs_title), + navigationIcon = { + BackButton(onClick = onBack, enabled = !state.isApplyingChanges) + } + ) + }, + header = { + IconTitleSubtitleMolecule( + modifier = Modifier.padding(top = 24.dp, start = 24.dp, end = 24.dp), + title = stringResource(R.string.screen_labs_header_title), + subTitle = stringResource(R.string.screen_labs_header_description), + iconStyle = BigIcon.Style.Default(CompoundIcons.Labs()) + ) + }, + contentPadding = PaddingValues(), + content = { + LazyColumn( + modifier = Modifier.fillMaxWidth(), + contentPadding = PaddingValues(horizontal = 10.dp, vertical = 20.dp), + ) { + items(items = state.features, key = { it.key }) { feature -> + SwitchListItem( + leadingContent = feature.icon?.let { ListItemContent.Icon(it) }, + headline = feature.title, + supportingText = feature.description, + value = feature.isEnabled, + onChange = { + state.eventSink(LabsEvents.ToggleFeature(feature)) + } + ) + } + } + } + ) +} + +@PreviewsDayNight +@Composable +internal fun LabsViewPreview(@PreviewParameter(LabsStateProvider::class) state: LabsState) { + ElementPreview { + LabsView(state = state, onBack = {}) + } +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt index 4303d6394e..f488889c5b 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt @@ -14,12 +14,12 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class NotificationSettingsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt index 3dd86ac224..ccba221d9a 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt @@ -14,7 +14,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs @@ -22,7 +22,7 @@ import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class EditDefaultNotificationSettingNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 fec0a5fdcc..25b062827f 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 @@ -17,7 +17,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runUpdatingStateNoSuccess @@ -37,7 +37,7 @@ import kotlinx.coroutines.launch import java.text.Collator import kotlin.time.Duration.Companion.seconds -@Inject +@AssistedInject class EditDefaultNotificationSettingPresenter( private val notificationSettingsService: NotificationSettingsService, @Assisted private val isOneToOne: Boolean, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootEvents.kt index ff74cebb51..87074ec7f9 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootEvents.kt @@ -7,6 +7,9 @@ package io.element.android.features.preferences.impl.root +import io.element.android.libraries.matrix.api.core.SessionId + sealed interface PreferencesRootEvents { data object OnVersionInfoClick : PreferencesRootEvents + data class SwitchToSession(val sessionId: SessionId) : PreferencesRootEvents } 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 76febd58af..67d50a76f0 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 @@ -16,7 +16,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +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 @@ -26,7 +26,7 @@ import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.user.MatrixUser @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class PreferencesRootNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, @@ -34,6 +34,7 @@ class PreferencesRootNode( private val directLogoutView: DirectLogoutView, ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { + fun onAddAccount() fun onOpenBugReport() fun onSecureBackupClick() fun onOpenAnalytics() @@ -42,12 +43,17 @@ class PreferencesRootNode( fun onOpenNotificationSettings() fun onOpenLockScreenSettings() fun onOpenAdvancedSettings() + fun onOpenLabs() fun onOpenUserProfile(matrixUser: MatrixUser) fun onOpenBlockedUsers() fun onSignOutClick() fun onOpenAccountDeactivation() } + private fun onAddAccount() { + plugins().forEach { it.onAddAccount() } + } + private fun onOpenBugReport() { plugins().forEach { it.onOpenBugReport() } } @@ -64,6 +70,10 @@ class PreferencesRootNode( plugins().forEach { it.onOpenAdvancedSettings() } } + private fun onOpenLabs() { + plugins().forEach { it.onOpenLabs() } + } + private fun onOpenAnalytics() { plugins().forEach { it.onOpenAnalytics() } } @@ -119,12 +129,14 @@ class PreferencesRootNode( state = state, modifier = modifier, onBackClick = this::navigateUp, + onAddAccountClick = this::onAddAccount, onOpenRageShake = this::onOpenBugReport, onOpenAnalytics = this::onOpenAnalytics, onOpenAbout = this::onOpenAbout, onSecureBackupClick = this::onSecureBackupClick, onOpenDeveloperSettings = this::onOpenDeveloperSettings, onOpenAdvancedSettings = this::onOpenAdvancedSettings, + onOpenLabs = this::onOpenLabs, onManageAccountClick = { onManageAccountClick(activity, it, isDark) }, onOpenNotificationSettings = this::onOpenNotificationSettings, onOpenLockScreenSettings = this::onOpenLockScreenSettings, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt index aad8086df6..3da72f982e 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt @@ -24,13 +24,21 @@ import io.element.android.features.rageshake.api.RageshakeFeatureAvailability import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.indicator.api.IndicatorService import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.oidc.AccountManagementAction +import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.verification.SessionVerificationService +import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.services.analytics.api.AnalyticsService +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -45,6 +53,8 @@ class PreferencesRootPresenter( private val directLogoutPresenter: Presenter, private val showDeveloperSettingsProvider: ShowDeveloperSettingsProvider, private val rageshakeFeatureAvailability: RageshakeFeatureAvailability, + private val featureFlagService: FeatureFlagService, + private val sessionStore: SessionStore, ) : Presenter { @Composable override fun present(): PreferencesRootState { @@ -55,6 +65,25 @@ class PreferencesRootPresenter( matrixClient.getUserProfile() } + val isMultiAccountEnabled by remember { + featureFlagService.isFeatureEnabledFlow(FeatureFlags.MultiAccount) + }.collectAsState(initial = false) + + val otherSessions by remember { + sessionStore.sessionsFlow().map { list -> + list + .filter { it.userId != matrixClient.sessionId.value } + .map { + MatrixUser( + userId = UserId(it.userId), + displayName = it.userDisplayName, + avatarUrl = it.userAvatarUrl, + ) + } + .toImmutableList() + } + }.collectAsState(initial = persistentListOf()) + val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() val hasAnalyticsProviders = remember { analyticsService.getAvailableAnalyticsProviders().isNotEmpty() } @@ -83,6 +112,8 @@ class PreferencesRootPresenter( .launchIn(this) } + val showLabsItem = remember { featureFlagService.getAvailableFeatures().any { it.isInLabs && !it.isFinished } } + val directLogoutState = directLogoutPresenter.present() LaunchedEffect(Unit) { @@ -96,6 +127,9 @@ class PreferencesRootPresenter( is PreferencesRootEvents.OnVersionInfoClick -> { showDeveloperSettingsProvider.unlockDeveloperSettings(coroutineScope) } + is PreferencesRootEvents.SwitchToSession -> coroutineScope.launch { + sessionStore.setLatestSession(event.sessionId.value) + } } } @@ -103,6 +137,8 @@ class PreferencesRootPresenter( myUser = matrixUser.value, version = versionFormatter.get(), deviceId = matrixClient.deviceId, + isMultiAccountEnabled = isMultiAccountEnabled, + otherSessions = otherSessions, showSecureBackup = !canVerifyUserSession, showSecureBackupBadge = showSecureBackupIndicator, accountManagementUrl = accountManagementUrl.value, @@ -112,6 +148,7 @@ class PreferencesRootPresenter( showDeveloperSettings = showDeveloperSettings, canDeactivateAccount = canDeactivateAccount, showBlockedUsersItem = showBlockedUsersItem, + showLabsItem = showLabsItem, directLogoutState = directLogoutState, snackbarMessage = snackbarMessage, eventSink = ::handleEvent, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt index ebe8aaf57f..3df13e0efd 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt @@ -11,11 +11,14 @@ import io.element.android.features.logout.api.direct.DirectLogoutState import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.user.MatrixUser +import kotlinx.collections.immutable.ImmutableList data class PreferencesRootState( val myUser: MatrixUser, val version: String, val deviceId: DeviceId?, + val isMultiAccountEnabled: Boolean, + val otherSessions: ImmutableList, val showSecureBackup: Boolean, val showSecureBackupBadge: Boolean, val accountManagementUrl: String?, @@ -25,6 +28,7 @@ data class PreferencesRootState( val showDeveloperSettings: Boolean, val canDeactivateAccount: Boolean, val showBlockedUsersItem: Boolean, + val showLabsItem: Boolean, val directLogoutState: DirectLogoutState, val snackbarMessage: SnackbarMessage?, val eventSink: (PreferencesRootEvents) -> Unit, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt index 91b32fe12d..ab30d07bcf 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt @@ -11,15 +11,20 @@ import io.element.android.features.logout.api.direct.aDirectLogoutState import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.toImmutableList fun aPreferencesRootState( - myUser: MatrixUser, + myUser: MatrixUser = aMatrixUser(), + otherSessions: List = emptyList(), eventSink: (PreferencesRootEvents) -> Unit = { _ -> }, ) = PreferencesRootState( myUser = myUser, version = "Version 1.1 (1)", deviceId = DeviceId("ILAKNDNASDLK"), + isMultiAccountEnabled = true, + otherSessions = otherSessions.toImmutableList(), showSecureBackup = true, showSecureBackupBadge = true, accountManagementUrl = "aUrl", @@ -28,6 +33,7 @@ fun aPreferencesRootState( canReportBug = true, showDeveloperSettings = true, showBlockedUsersItem = true, + showLabsItem = true, canDeactivateAccount = true, snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete), directLogoutState = aDirectLogoutState(), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt index 544b5f5b3e..3b4d2d2670 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt @@ -8,6 +8,7 @@ package io.element.android.features.preferences.impl.root import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable @@ -23,11 +24,14 @@ import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.preferences.impl.R import io.element.android.features.preferences.impl.user.UserPreferences import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage +import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.preferences.PreferencePage +import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.ListItem @@ -38,12 +42,15 @@ import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbar import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.MatrixUserProvider +import io.element.android.libraries.matrix.ui.components.MatrixUserRow +import io.element.android.libraries.matrix.ui.components.aMatrixUserList import io.element.android.libraries.ui.strings.CommonStrings @Composable fun PreferencesRootView( state: PreferencesRootState, onBackClick: () -> Unit, + onAddAccountClick: () -> Unit, onSecureBackupClick: () -> Unit, onManageAccountClick: (url: String) -> Unit, onOpenAnalytics: () -> Unit, @@ -52,6 +59,7 @@ fun PreferencesRootView( onOpenAbout: () -> Unit, onOpenDeveloperSettings: () -> Unit, onOpenAdvancedSettings: () -> Unit, + onOpenLabs: () -> Unit, onOpenNotificationSettings: () -> Unit, onOpenUserProfile: (MatrixUser) -> Unit, onOpenBlockedUsers: () -> Unit, @@ -74,7 +82,12 @@ fun PreferencesRootView( }, user = state.myUser, ) - + if (state.isMultiAccountEnabled) { + MultiAccountSection( + state = state, + onAddAccountClick = onAddAccountClick, + ) + } // 'Manage my app' section ManageAppSection( state = state, @@ -98,6 +111,7 @@ fun PreferencesRootView( onOpenRageShake = onOpenRageShake, onOpenAdvancedSettings = onOpenAdvancedSettings, onOpenDeveloperSettings = onOpenDeveloperSettings, + onOpenLabs = onOpenLabs, onSignOutClick = onSignOutClick, onDeactivateClick = onDeactivateClick, ) @@ -114,6 +128,38 @@ fun PreferencesRootView( } } +@Composable +private fun ColumnScope.MultiAccountSection( + state: PreferencesRootState, + onAddAccountClick: () -> Unit, +) { + HorizontalDivider( + thickness = 8.dp, + color = ElementTheme.colors.bgSubtleSecondary, + ) + state.otherSessions.forEach { matrixUser -> + MatrixUserRow( + modifier = Modifier.clickable { + state.eventSink(PreferencesRootEvents.SwitchToSession(matrixUser.userId)) + }, + matrixUser = matrixUser, + avatarSize = AvatarSize.AccountItem, + ) + HorizontalDivider() + } + ListItem( + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Plus())), + headlineContent = { + Text(stringResource(CommonStrings.common_add_another_account)) + }, + onClick = onAddAccountClick, + ) + HorizontalDivider( + thickness = 8.dp, + color = ElementTheme.colors.bgSubtleSecondary, + ) +} + @Composable private fun ColumnScope.ManageAppSection( state: PreferencesRootState, @@ -186,6 +232,7 @@ private fun ColumnScope.GeneralSection( onOpenAnalytics: () -> Unit, onOpenRageShake: () -> Unit, onOpenAdvancedSettings: () -> Unit, + onOpenLabs: () -> Unit, onOpenDeveloperSettings: () -> Unit, onSignOutClick: () -> Unit, onDeactivateClick: () -> Unit, @@ -214,6 +261,15 @@ private fun ColumnScope.GeneralSection( leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Settings())), onClick = onOpenAdvancedSettings, ) + + if (state.showLabsItem) { + ListItem( + headlineContent = { Text(stringResource(id = R.string.screen_labs_title)) }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Labs())), + onClick = onOpenLabs, + ) + } + ListItem( headlineContent = { Text(stringResource(id = CommonStrings.action_signout)) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.SignOut())), @@ -287,10 +343,12 @@ private fun ContentToPreview(matrixUser: MatrixUser) { PreferencesRootView( state = aPreferencesRootState(myUser = matrixUser), onBackClick = {}, + onAddAccountClick = {}, onOpenAnalytics = {}, onOpenRageShake = {}, onOpenDeveloperSettings = {}, onOpenAdvancedSettings = {}, + onOpenLabs = {}, onOpenAbout = {}, onSecureBackupClick = {}, onManageAccountClick = {}, @@ -302,3 +360,16 @@ private fun ContentToPreview(matrixUser: MatrixUser) { onDeactivateClick = {}, ) } + +@PreviewsDayNight +@Composable +internal fun MultiAccountSectionPreview() = ElementPreview { + Column { + MultiAccountSection( + state = aPreferencesRootState( + otherSessions = aMatrixUserList(), + ), + onAddAccountClick = {}, + ) + } +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileNode.kt index 64d3e73447..d691508427 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileNode.kt @@ -13,7 +13,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs @@ -21,7 +21,7 @@ import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.user.MatrixUser @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class EditUserProfileNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt index f6069bc442..0cd0144986 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt @@ -21,7 +21,7 @@ import androidx.compose.runtime.setValue import androidx.core.net.toUri import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.androidutils.file.TemporaryUriDeleter import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter @@ -41,7 +41,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import timber.log.Timber -@Inject +@AssistedInject class EditUserProfilePresenter( @Assisted private val matrixUser: MatrixUser, private val matrixClient: MatrixClient, diff --git a/features/preferences/impl/src/main/res/values-bg/translations.xml b/features/preferences/impl/src/main/res/values-bg/translations.xml index 289c59d360..bfcab248c6 100644 --- a/features/preferences/impl/src/main/res/values-bg/translations.xml +++ b/features/preferences/impl/src/main/res/values-bg/translations.xml @@ -1,15 +1,25 @@ "Изберете как да получавате известия" + "Режим за програмисти" + "Активирайте, за да имате достъп до функции и функционалности за програмисти." "Скриване на профилните снимки в заявките за покана за стая" + "Качвайте снимки и видеоклипове по-бързо и намалете използването на данни" "Оптимизиране на качеството на медията" "Модерация и безопасност" + "Изключете редактора за форматиран текст, за да пишете Markdown ръчно." "Потвърждения за прочитане" + "Ако е изключено, вашите потвърждения за прочитане няма да бъдат изпращани на никого. Все още ще получавате потвърждения за прочитане от други потребители." "Споделяне на присъствието" + "Ако е изключено, няма да можете да изпращате или получавате потвърждения за прочитане или известия за писане." "Скриване винаги" "Показване винаги" "В частни стаи" + "Скрита мултимедия винаги може да бъде показана, като се докосне" + "Показване на мултимедия в хронологията" + "Активиране на опцията за преглед на изходния код на съобщението в хронологията." "Отблокиране" + "Ще можете да виждате отново всички съобщения от тях." "Отблокиране на потребителя" "Име" "Вашето Име" @@ -18,13 +28,17 @@ "Редактиране на профила" "Обновяване на профила…" "Допълнителни настройки" + "Аудио и видео разговори" + "Несъответствие в конфигурацията" "Директни чатове" "Персонализирана настройка за чат" + "Възникна грешка при обновяването на настройките за известия." "Всички съобщения" "Само споменавания и ключови думи" "В директни чатове да бъда известяван за" "В групови чатове да бъда известяван за" "Включване на известията на това устройство" + "Конфигурацията не е оправена, моля, опитайте отново." "Групови чатове" "Покани" "Вашият сървър не поддържа тази опция в шифровани стаи, може да не получавате известия в някои стаи." @@ -34,5 +48,8 @@ "Известяване за @room" "За да получавате известия, моля, променете своя %1$s" "системни настройки" + "Системните известия са изключени" "Известия" + "Отстраняване на неизправности" + "Отстраняване на неизправности с известията" diff --git a/features/preferences/impl/src/main/res/values-da/translations.xml b/features/preferences/impl/src/main/res/values-da/translations.xml index acf9fe4087..f258fb0952 100644 --- a/features/preferences/impl/src/main/res/values-da/translations.xml +++ b/features/preferences/impl/src/main/res/values-da/translations.xml @@ -10,6 +10,7 @@ "Ugyldig URL, sørg for at inkludere protokollen (http/https) og den korrekte adresse." "Skjul avatarer i anmodninger om invitation til rum" "Skjul forhåndsvisning af medier i tidslinjen" + "Laboratorier" "Upload fotos og videoer hurtigere, og reducér dataforbrug" "Optimér mediekvaliteten" "Moderation og sikkerhed" @@ -43,6 +44,11 @@ "Kan ikke opdatere profilen" "Redigér profil" "Opdaterer profil…" + "Aktivér svar-tråde" + "Appen genstarter for at anvende denne ændring." + "Prøv vores nyeste idéer under udvikling. Disse funktioner er ikke færdige; de ​​kan være ustabile og kan ændre sig." + "Er du i humør til at eksperimentere?" + "Laboratorier" "Yderligere indstillinger" "Lyd- og videoopkald" "Uoverensstemmelse i konfigurationen" diff --git a/features/preferences/impl/src/main/res/values-de/translations.xml b/features/preferences/impl/src/main/res/values-de/translations.xml index 4072207fb4..c3722ec88c 100644 --- a/features/preferences/impl/src/main/res/values-de/translations.xml +++ b/features/preferences/impl/src/main/res/values-de/translations.xml @@ -10,6 +10,7 @@ "Ungültige URL, bitte gib das Protokoll (http/https) und die richtige Adresse an." "Avatare in Chateinladungen ausblenden" "Medienvorschau im Nachrichtenverlauf ausblenden" + "Labs" "Lade Fotos und Videos schneller hoch und reduziere den Datenverbrauch" "Optimiere die Medienqualität" "Moderation und Sicherheit" @@ -43,6 +44,11 @@ "Profil kann nicht aktualisiert werden" "Profil bearbeiten" "Profil wird aktualisiert…" + "Thread-Antworten aktivieren" + "Die App wird neu gestartet, um diese Änderung zu übernehmen." + "Probier unsere neuesten Ideen in der Entwicklung aus. Diese Funktionen sind noch nicht fertiggestellt; sie können instabil sein und sich noch ändern." + "Entdeckungsfreudig?" + "Labs" "Zusätzliche Einstellungen" "Audio- und Videoanrufe" "Konfiguration stimmt nicht überein" diff --git a/features/preferences/impl/src/main/res/values-fa/translations.xml b/features/preferences/impl/src/main/res/values-fa/translations.xml index 1ca2ad38db..22ff843dc2 100644 --- a/features/preferences/impl/src/main/res/values-fa/translations.xml +++ b/features/preferences/impl/src/main/res/values-fa/translations.xml @@ -58,7 +58,7 @@ "تنظیمات سامانه" "آگاهی‌های سامانه‌ای خاموش شدند" "آگاهی‌ها" - "تاریخچهٔ فرستادن" + "تاریخچهٔ آگاهی‌های ارسالی" "رفع‌اشکال" "رفع‌اشکال آگاهی‌ها" diff --git a/features/preferences/impl/src/main/res/values-fi/translations.xml b/features/preferences/impl/src/main/res/values-fi/translations.xml index 1240b620e9..f1462f3bb7 100644 --- a/features/preferences/impl/src/main/res/values-fi/translations.xml +++ b/features/preferences/impl/src/main/res/values-fi/translations.xml @@ -10,6 +10,7 @@ "Virheellinen URL-osoite. Varmista, että sisällytät protokollan (http/https) ja oikean osoitteen." "Piilota huoneiden avatarit kutsuista" "Piilota median esikatselu aikajanalla" + "Labrat" "Lähetä valokuvia ja videoita nopeammin ja vähennä datan käyttöä." "Optimoi median laatu" "Moderointi ja Turvallisuus" @@ -43,6 +44,11 @@ "Profiilin muokkaaminen ei onnistunut" "Muokkaa profiilia" "Muokataan profiilia…" + "Ota käyttöön viestiketjuvastaukset" + "Sovellus käynnistyy uudelleen muutoksen käyttöönottamiseksi." + "Kokeile uusimpia kehitteillä olevia ideoitamme. Nämä ominaisuudet eivät ole vielä valmiita; ne voivat olla epävakaita ja muuttua." + "Kokeilunhaluinen olo?" + "Labrat" "Lisäasetukset" "Ääni- ja videopuheluista" "Konfiguraatio ei täsmää" diff --git a/features/preferences/impl/src/main/res/values-fr/translations.xml b/features/preferences/impl/src/main/res/values-fr/translations.xml index dbb0b13326..5ee91388bd 100644 --- a/features/preferences/impl/src/main/res/values-fr/translations.xml +++ b/features/preferences/impl/src/main/res/values-fr/translations.xml @@ -10,6 +10,7 @@ "URL invalide, assurez-vous d’inclure le protocol (http/https) et l’adresse correcte." "Masquer les avatars des salons dans les invitations" "Masquer les aperçus des médias dans les discussions" + "Expérimental" "Téléchargez des photos et des vidéos plus rapidement et réduisez la consommation de données" "Optimisez la qualité des médias" "Modération et sécurité" @@ -43,6 +44,11 @@ "Impossible de mettre à jour le profil" "Modifier le profil" "Mise à jour du profil…" + "Activez les fils de discussion." + "Un changement entraînera le redémarrage de l’application." + "Découvrez nos dernières idées en cours de développement. Ces fonctionnalités ne sont pas encore finalisées; elles peuvent être instables et évoluer." + "Envie d’expérimenter?" + "Expérimental" "Réglages supplémentaires" "Appels audio et vidéo" "Incompatibilité de configuration" diff --git a/features/preferences/impl/src/main/res/values-hu/translations.xml b/features/preferences/impl/src/main/res/values-hu/translations.xml index d64864549d..9cc5a97a44 100644 --- a/features/preferences/impl/src/main/res/values-hu/translations.xml +++ b/features/preferences/impl/src/main/res/values-hu/translations.xml @@ -34,8 +34,8 @@ "Engedélyezze a beállítást az üzenet forrásának megjelenítéséhez az idővonalon." "Nincsenek letiltott felhasználók" "Letiltás feloldása" - "Újra láthatja az összes üzenetét." - "Felhasználó kitiltásának feloldása" + "Újra látni fogja az összes üzenetét." + "Felhasználó letiltásának feloldása" "Tiltás feloldása…" "Megjelenítendő név" "Saját megjelenítendő név" @@ -70,7 +70,7 @@ Ha folytatja, egyes beállítások megváltozhatnak." "rendszerbeállításokat" "A rendszerértesítések ki vannak kapcsolva" "Értesítések" - "Leküldéses értesítés előzmények" + "Leküldéses értesítések előzmények" "Hibaelhárítás" "Értesítések hibaelhárítása" diff --git a/features/preferences/impl/src/main/res/values-nb/translations.xml b/features/preferences/impl/src/main/res/values-nb/translations.xml index 84283b898b..90ec12a1a1 100644 --- a/features/preferences/impl/src/main/res/values-nb/translations.xml +++ b/features/preferences/impl/src/main/res/values-nb/translations.xml @@ -10,9 +10,17 @@ "Ugyldig URL. Pass på at du inkluderer protokollen (http/https) og riktig adresse." "Skjul avatarer i invitasjonsforespørsler til rom" "Skjul forhåndsvisninger av medier på tidslinjen" + "Prøvefunksjoner" "Last opp bilder og videoer raskere og reduser databruken" "Optimaliser mediekvaliteten" "Moderasjon og sikkerhet" + "Optimaliser bilder automatisk for raskere opplastinger og mindre filstørrelser." + "Optimaliser kvaliteten på bildeopplasting" + "%1$s. Trykk her for å endre." + "Høy (1080p)" + "Lav (480p)" + "Standard (720p)" + "Kvalitet på videoopplasting" "Leverandør av pushvarsling" "Deaktiver rik tekstredigering for å skrive Markdown manuelt." "Lesebekreftelser" @@ -36,6 +44,11 @@ "Kan ikke oppdatere profilen" "Rediger profil" "Oppdaterer profilen…" + "Aktiver trådsvar" + "Appen vil starte på nytt for å implementere denne endringen." + "Prøv ut våre nyeste ideer under utvikling. Disse funksjonene er ikke ferdig utviklet; de kan være ustabile og kan endres." + "Lyst til å prøve noe nytt?" + "Prøvefunksjoner" "Ytterligere innstillinger" "Lyd- og videosamtaler" "Uoverensstemmelse i konfigurasjonen" diff --git a/features/preferences/impl/src/main/res/values-pt/translations.xml b/features/preferences/impl/src/main/res/values-pt/translations.xml index f80947d2f7..8ece6bde9e 100644 --- a/features/preferences/impl/src/main/res/values-pt/translations.xml +++ b/features/preferences/impl/src/main/res/values-pt/translations.xml @@ -70,7 +70,7 @@ Se prosseguires, algumas delas podem ser alteradas." "configurações do sistema" "Notificações do sistema desativadas" "Notificações" - "Histórico de notificações" + "Histórico de push" "Resolução de problemas" "Corrigir notificações" diff --git a/features/preferences/impl/src/main/res/values-ro/translations.xml b/features/preferences/impl/src/main/res/values-ro/translations.xml index 5977771ee6..6f8e41c5b9 100644 --- a/features/preferences/impl/src/main/res/values-ro/translations.xml +++ b/features/preferences/impl/src/main/res/values-ro/translations.xml @@ -10,6 +10,7 @@ "URL invalid, vă rugăm să vă asigurați că includeți protocolul (http/https) și adresa corectă." "Ascundeți avatarele din invitațiile pentru camere" "Ascundeți previzualizările media în lista de mesaje" + "Laboratoare" "Încărcați fotografii și videoclipuri mai rapid și reduceți consumul de date" "Optimizați calitatea media" "Moderare și siguranță" @@ -43,6 +44,11 @@ "Nu s-a putut actualiza profilul" "Editați profilul" "Se actualizează profilul…" + "Activați răspunsurile în fir" + "Aplicația va reporni pentru a aplica această modificare." + "Încercați cele mai noi idei în curs de dezvoltare. Aceste funcții nu sunt finalizate; pot fi instabile și pot suferi modificări." + "Doriți experiențe noi?" + "Laboratoare" "Setări adiționale" "Apeluri audio și video" "Nepotrivire de configurație" diff --git a/features/preferences/impl/src/main/res/values-ru/translations.xml b/features/preferences/impl/src/main/res/values-ru/translations.xml index 59d19b0210..6ac5a5d03c 100644 --- a/features/preferences/impl/src/main/res/values-ru/translations.xml +++ b/features/preferences/impl/src/main/res/values-ru/translations.xml @@ -10,9 +10,13 @@ "Адрес указан неверно, удостоверьтесь, что вы указали протокол (http/https) и правильный адрес." "Скрыть аватары в запросах на приглашение в комнату" "Скрыть предварительный просмотр медиафайлов на временной шкале" + "Лаборатория" "Загружайте фотографии и видео быстрее и сокращайте потребление трафика" "Оптимизировать качество мультимедиа" "Модерация и безопасность" + "Автоматически оптимизируйте изображения для более быстрой загрузки и уменьшения размера файлов." + "Оптимизируйте качество загрузки изображения" + "%1$s. Нажмите здесь, чтобы изменить." "Высокое (1080p)" "Низкое (480p)" "Среднее (720p)" @@ -40,6 +44,10 @@ "Невозможно обновить профиль" "Редактировать профиль" "Обновление профиля…" + "Включить ответы в топике" + "Попробуйте наши последние идеи в разработке. Эти функции ещё не завершены, они могут быть нестабильны и могут измениться." + "Хотите попробовать?" + "Лаборатория" "Дополнительные параметры" "Аудио и видео звонки" "Несоответствие конфигурации" diff --git a/features/preferences/impl/src/main/res/values-zh/translations.xml b/features/preferences/impl/src/main/res/values-zh/translations.xml index d9f5bb3c88..7ac8952090 100644 --- a/features/preferences/impl/src/main/res/values-zh/translations.xml +++ b/features/preferences/impl/src/main/res/values-zh/translations.xml @@ -10,6 +10,7 @@ "URL 无效,请确保包含协议(http/https)和正确的地址。" "在房间邀请请求中隐藏头像" "在时间轴中隐藏媒体预览" + "实验室" "针对上传进行优化" "媒体" "内容审核与安全" @@ -43,6 +44,7 @@ "无法更新个人资料" "编辑个人资料" "更新个人资料……" + "实验室" "更多设置" "音视频通话" "配置不匹配" diff --git a/features/preferences/impl/src/main/res/values/localazy.xml b/features/preferences/impl/src/main/res/values/localazy.xml index 8b3c9d1d82..31ee676226 100644 --- a/features/preferences/impl/src/main/res/values/localazy.xml +++ b/features/preferences/impl/src/main/res/values/localazy.xml @@ -10,6 +10,7 @@ "Invalid URL, please make sure you include the protocol (http/https) and the correct address." "Hide avatars in room invite requests" "Hide media previews in timeline" + "Labs" "Upload photos and videos faster and reduce data usage" "Optimise media quality" "Moderation and Safety" @@ -43,6 +44,11 @@ "Unable to update profile" "Edit profile" "Updating profile…" + "Enable thread replies" + "The app will restart to apply this change." + "Try out our latest ideas in development. These features are not finalised; they may be unstable, may change." + "Feeling experimental?" + "Labs" "Additional settings" "Audio and video calls" "Configuration mismatch" 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 807ff3a5c6..9e1bd70376 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 @@ -20,7 +20,6 @@ import io.element.android.features.logout.api.LogoutEntryPoint 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.matrix.api.core.SessionId import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint import io.element.android.libraries.troubleshoot.api.PushHistoryEntryPoint import io.element.android.tests.testutils.lambda.lambdaError @@ -64,10 +63,11 @@ class DefaultPreferencesEntryPointTest { ) } val callback = object : PreferencesEntryPoint.Callback { + override fun onAddAccount() = lambdaError() override fun onOpenBugReport() = lambdaError() override fun onSecureBackupClick() = lambdaError() override fun onOpenRoomNotificationSettings(roomId: RoomId) = lambdaError() - override fun navigateTo(sessionId: SessionId, roomId: RoomId, eventId: EventId) = lambdaError() + override fun navigateTo(roomId: RoomId, eventId: EventId) = lambdaError() } val params = PreferencesEntryPoint.Params( initialElement = PreferencesEntryPoint.InitialTarget.NotificationSettings, 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 9e2c8ca175..76b22871f0 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 @@ -19,6 +19,7 @@ import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.featureflag.test.FakeFeature import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore @@ -35,7 +36,16 @@ class DeveloperSettingsPresenterTest { @Test fun `present - ensures initial states are correct`() = runTest { - val presenter = createDeveloperSettingsPresenter() + val availableFeatures = listOf( + FakeFeature( + key = "feature_1", + title = "Feature 1", + isInLabs = false, + ) + ) + val presenter = createDeveloperSettingsPresenter( + featureFlagService = FakeFeatureFlagService(providedAvailableFeatures = availableFeatures) + ) presenter.test { awaitItem().also { state -> assertThat(state.features).isEmpty() @@ -50,8 +60,7 @@ class DeveloperSettingsPresenterTest { } awaitItem().also { state -> assertThat(state.features).isNotEmpty() - val numberOfModifiableFeatureFlags = FeatureFlags.entries.count { it.isFinished.not() } - assertThat(state.features).hasSize(numberOfModifiableFeatureFlags) + assertThat(state.features).hasSize(1) assertThat(state.tracingLogLevel.dataOrNull()).isEqualTo(LogLevelItem.INFO) } awaitItem().also { state -> @@ -161,8 +170,51 @@ class DeveloperSettingsPresenterTest { } } + @Test + fun `present - won't display features in labs or finished`() = runTest { + val availableFeatures = listOf( + // Only this feature should be displayed + FakeFeature( + key = "feature_1", + title = "Feature 1", + isInLabs = false, + ), + FakeFeature( + key = "feature_2", + title = "Feature 2", + isInLabs = true, + ), + FakeFeature( + key = "feature_3", + title = "Feature 3", + isInLabs = false, + isFinished = true, + ) + ) + + val presenter = createDeveloperSettingsPresenter( + featureFlagService = FakeFeatureFlagService( + providedAvailableFeatures = availableFeatures, + ) + ) + presenter.test { + skipItems(2) + awaitItem().also { state -> + assertThat(state.features).hasSize(1) + } + } + } + private fun createDeveloperSettingsPresenter( - featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(), + featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService( + providedAvailableFeatures = listOf( + FakeFeature( + key = "feature_1", + title = "Feature 1", + isInLabs = false, + ) + ) + ), cacheSizeUseCase: FakeComputeCacheSizeUseCase = FakeComputeCacheSizeUseCase(), clearCacheUseCase: FakeClearCacheUseCase = FakeClearCacheUseCase(), preferencesStore: InMemoryAppPreferencesStore = InMemoryAppPreferencesStore(), 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 new file mode 100644 index 0000000000..5117a9fe94 --- /dev/null +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/labs/LabsPresenterTest.kt @@ -0,0 +1,126 @@ +/* + * Copyright 2025 New Vector 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.labs + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.preferences.impl.tasks.ClearCacheUseCase +import io.element.android.features.preferences.impl.tasks.FakeClearCacheUseCase +import io.element.android.libraries.featureflag.api.Feature +import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.featureflag.test.FakeFeature +import io.element.android.libraries.featureflag.test.FakeFeatureFlagService +import io.element.android.services.toolbox.test.strings.FakeStringProvider +import io.element.android.tests.testutils.test +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class LabsPresenterTest { + @Test + fun `present - ensures only unfinished features in labs are displayed`() = runTest { + val availableFeatures = listOf( + FakeFeature( + key = "feature_1", + title = "Feature 1", + isInLabs = true, + ), + FakeFeature( + key = "feature_2", + title = "Feature 2", + isInLabs = false, + ), + FakeFeature( + key = "feature_3", + title = "Feature 3", + isInLabs = true, + isFinished = true, + ) + ) + createLabsPresenter( + availableFeatures = availableFeatures, + ).test { + val receivedFeatures = awaitItem().features + assertThat(receivedFeatures).hasSize(1) + assertThat(receivedFeatures.first().key).isEqualTo(availableFeatures.first().key) + + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - ToggleFeature actually toggles the value`() = runTest { + val availableFeatures = listOf( + FakeFeature( + key = "feature_1", + title = "Feature 1", + isInLabs = true, + ), + ) + createLabsPresenter( + availableFeatures = availableFeatures, + ).test { + val initialItem = awaitItem() + val feature = initialItem.features.first() + assertThat(feature.isEnabled).isFalse() + + // Wait until the data finished loading + skipItems(1) + + // Toggle the feature, should be true now + initialItem.eventSink(LabsEvents.ToggleFeature(feature)) + assertThat(awaitItem().features.first().isEnabled).isTrue() + + // Toggle the feature, should be false now + initialItem.eventSink(LabsEvents.ToggleFeature(feature)) + assertThat(awaitItem().features.first().isEnabled).isFalse() + } + } + + @Test + fun `present - ToggleFeature with the 'Threads' feature resets the cache`() = runTest { + val availableFeatures = listOf( + FakeFeature( + key = FeatureFlags.Threads.key, + title = "Threads", + isInLabs = true, + ), + ) + + val clearCacheUseCase = FakeClearCacheUseCase() + createLabsPresenter( + availableFeatures = availableFeatures, + clearCacheUseCase = clearCacheUseCase, + ).test { + val initialItem = awaitItem() + val feature = initialItem.features.first() + assertThat(feature.isEnabled).isFalse() + assertThat(initialItem.isApplyingChanges).isFalse() + + // Wait until the data finished loading + skipItems(1) + + // Toggle the feature + initialItem.eventSink(LabsEvents.ToggleFeature(feature)) + assertThat(awaitItem().features.first().isEnabled).isTrue() + + // The clear cache use case should have been called + assertThat(awaitItem().isApplyingChanges).isTrue() + assertThat(clearCacheUseCase.executeHasBeenCalled).isTrue() + } + } + + private fun createLabsPresenter( + availableFeatures: List = emptyList(), + clearCacheUseCase: ClearCacheUseCase = FakeClearCacheUseCase(), + ): LabsPresenter { + return LabsPresenter( + stringProvider = FakeStringProvider(), + featureFlagService = FakeFeatureFlagService(providedAvailableFeatures = availableFeatures), + clearCacheUseCase = clearCacheUseCase, + ) + } +} diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt index 42fee711a7..f65589beb6 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt @@ -16,15 +16,24 @@ import io.element.android.features.preferences.impl.utils.ShowDeveloperSettingsP import io.element.android.features.rageshake.api.RageshakeFeatureAvailability import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.featureflag.test.FakeFeature +import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.indicator.api.IndicatorService import io.element.android.libraries.indicator.test.FakeIndicatorService import io.element.android.libraries.matrix.api.oidc.AccountManagementAction import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_AVATAR_URL +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_USER_NAME import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService +import io.element.android.libraries.sessionstorage.api.SessionStore +import io.element.android.libraries.sessionstorage.test.InMemorySessionStore +import io.element.android.libraries.sessionstorage.test.aSessionData import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaRecorder @@ -61,6 +70,8 @@ class PreferencesRootPresenterTest { ) ) assertThat(initialState.version).isEqualTo("A Version") + assertThat(initialState.isMultiAccountEnabled).isFalse() + assertThat(initialState.otherSessions).isEmpty() val loadedState = awaitItem() assertThat(loadedState.myUser).isEqualTo( MatrixUser( @@ -174,6 +185,80 @@ class PreferencesRootPresenterTest { } } + @Test + fun `present - labs can be shown if any feature flag is in labs and not finished`() = runTest { + createPresenter( + featureFlagService = FakeFeatureFlagService( + providedAvailableFeatures = listOf( + FakeFeature( + key = "feature_1", + title = "Feature 1", + isInLabs = true, + isFinished = false, + ) + ) + ), + matrixClient = FakeMatrixClient( + canDeactivateAccountResult = { true }, + accountManagementUrlResult = { Result.success(null) }, + ), + ).test { + assertThat(awaitItem().showLabsItem).isTrue() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - labs can't be shown if all feature flags in labs are finished`() = runTest { + createPresenter( + featureFlagService = FakeFeatureFlagService( + providedAvailableFeatures = listOf( + FakeFeature( + key = "feature_1", + title = "Feature 1", + isInLabs = true, + isFinished = true, + ) + ) + ), + matrixClient = FakeMatrixClient( + canDeactivateAccountResult = { true }, + accountManagementUrlResult = { Result.success(null) }, + ), + ).test { + assertThat(awaitItem().showLabsItem).isFalse() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - multiple accounts`() = runTest { + createPresenter( + matrixClient = FakeMatrixClient( + sessionId = A_SESSION_ID, + canDeactivateAccountResult = { true }, + ), + featureFlagService = FakeFeatureFlagService( + initialState = mapOf(FeatureFlags.MultiAccount.key to true) + ), + sessionStore = InMemorySessionStore( + initialList = listOf( + aSessionData(sessionId = A_SESSION_ID.value), + aSessionData( + sessionId = A_SESSION_ID_2.value, + userDisplayName = "Bob", + userAvatarUrl = "avatarUrl", + ), + ) + ) + ).test { + val state = awaitFirstItem() + assertThat(state.isMultiAccountEnabled).isTrue() + assertThat(state.otherSessions).hasSize(1) + assertThat(state.otherSessions[0]).isEqualTo(MatrixUser(userId = A_SESSION_ID_2, displayName = "Bob", avatarUrl = "avatarUrl")) + } + } + private suspend fun ReceiveTurbine.awaitFirstItem(): T { skipItems(1) return awaitItem() @@ -185,6 +270,8 @@ class PreferencesRootPresenterTest { showDeveloperSettingsProvider: ShowDeveloperSettingsProvider = ShowDeveloperSettingsProvider(aBuildMeta(BuildType.DEBUG)), rageshakeFeatureAvailability: RageshakeFeatureAvailability = RageshakeFeatureAvailability { flowOf(true) }, indicatorService: IndicatorService = FakeIndicatorService(), + featureFlagService: FeatureFlagService = FakeFeatureFlagService(), + sessionStore: SessionStore = InMemorySessionStore(), ) = PreferencesRootPresenter( matrixClient = matrixClient, sessionVerificationService = sessionVerificationService, @@ -195,5 +282,7 @@ class PreferencesRootPresenterTest { directLogoutPresenter = { aDirectLogoutState() }, showDeveloperSettingsProvider = showDeveloperSettingsProvider, rageshakeFeatureAvailability = rageshakeFeatureAvailability, + featureFlagService = featureFlagService, + sessionStore = sessionStore, ) } diff --git a/features/rageshake/api/src/main/res/values-bg/translations.xml b/features/rageshake/api/src/main/res/values-bg/translations.xml new file mode 100644 index 0000000000..c2a8c06adf --- /dev/null +++ b/features/rageshake/api/src/main/res/values-bg/translations.xml @@ -0,0 +1,4 @@ + + + "%1$s се срина при последното използване. Искате ли да споделите доклад за срива с нас?" + diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFlowNode.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFlowNode.kt index 0dd4d4f518..10af89f740 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFlowNode.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFlowNode.kt @@ -19,7 +19,7 @@ import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint import io.element.android.features.viewfolder.api.ViewFolderEntryPoint @@ -29,7 +29,7 @@ import io.element.android.libraries.architecture.createNode import kotlinx.parcelize.Parcelize @ContributesNode(AppScope::class) -@Inject +@AssistedInject class BugReportFlowNode( @Assisted val buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportNode.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportNode.kt index b6eb494589..e307dba8ec 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportNode.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportNode.kt @@ -16,14 +16,14 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.libraries.androidutils.system.toast import io.element.android.libraries.ui.strings.CommonStrings @ContributesNode(AppScope::class) -@Inject +@AssistedInject class BugReportNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt index 01b6857c0b..5787939688 100755 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt @@ -176,8 +176,8 @@ class DefaultBugReporter( .addFormDataPart("branch_name", buildMeta.gitBranchName) userId?.let { matrixClientProvider.getOrNull(it)?.let { client -> - val curveKey = client.encryptionService().deviceCurve25519() - val edKey = client.encryptionService().deviceEd25519() + val curveKey = client.encryptionService.deviceCurve25519() + val edKey = client.encryptionService.deviceEd25519() if (curveKey != null && edKey != null) { builder.addFormDataPart("device_keys", "curve25519:$curveKey, ed25519:$edKey") } diff --git a/features/rageshake/impl/src/main/res/values-bg/translations.xml b/features/rageshake/impl/src/main/res/values-bg/translations.xml index 50f792de00..7db9b494a4 100644 --- a/features/rageshake/impl/src/main/res/values-bg/translations.xml +++ b/features/rageshake/impl/src/main/res/values-bg/translations.xml @@ -11,5 +11,7 @@ "Изпращане на дневниците за сривове" "Разрешаване на дневниците" "Изпращане на екранна снимка" + "Дневниците ще бъдат включени към вашето съобщение, за да се уверим, че всичко работи правилно. За да изпратите съобщението си без дневници, изключете тази настройка." + "%1$s се срина при последното използване. Искате ли да споделите доклад за срива с нас?" "Преглед на дневниците" diff --git a/features/rageshake/impl/src/main/res/values-cs/translations.xml b/features/rageshake/impl/src/main/res/values-cs/translations.xml index 0ae9e585b1..93db5427d5 100644 --- a/features/rageshake/impl/src/main/res/values-cs/translations.xml +++ b/features/rageshake/impl/src/main/res/values-cs/translations.xml @@ -14,5 +14,7 @@ "Odeslat snímek obrazovky" "Protokoly budou součástí vaší zprávy, aby se zajistilo že vše funguje správně. Chcete-li odeslat zprávu bez protokolů, vypněte toto nastavení." "%1$s havaroval při posledním použití. Chcete se s námi podělit o zprávu o selhání?" + "Pokud máte problémy s oznámeními, nahrání nastavení oznámení nám může pomoci určit jejich příčinu." + "Nastavení odesílání oznámení" "Zobrazit protokoly" diff --git a/features/rageshake/impl/src/main/res/values-cy/translations.xml b/features/rageshake/impl/src/main/res/values-cy/translations.xml index 123c23ef7f..c9cee768e9 100644 --- a/features/rageshake/impl/src/main/res/values-cy/translations.xml +++ b/features/rageshake/impl/src/main/res/values-cy/translations.xml @@ -14,5 +14,7 @@ "Anfon luniau sgrin" "Bydd cofnodion yn cael eu cynnwys gyda\'ch neges i wneud yn siŵr bod popeth yn gweithio\'n iawn. I anfon eich neges heb logiau, diffoddwch y gosodiad hwn." "Chwalodd %1$s y tro diwethaf iddo gael ei ddefnyddio. Hoffech chi rannu adroddiad gwall gyda ni?" + "Os ydych chi\'n cael problemau gyda hysbysiadau, gall llwytho\'r gosodiadau hysbysiadau ein helpu i ddarganfod yr achos gwreiddiol." + "Anfon gosodiadau hysbysiadau" "Gweld logiau" diff --git a/features/rageshake/impl/src/main/res/values-da/translations.xml b/features/rageshake/impl/src/main/res/values-da/translations.xml index 47ee436e8b..035d28162f 100644 --- a/features/rageshake/impl/src/main/res/values-da/translations.xml +++ b/features/rageshake/impl/src/main/res/values-da/translations.xml @@ -14,5 +14,7 @@ "Send skærmbillede" "Logfiler vil blive inkluderet i din besked for at sikre, at alt fungerer korrekt. Hvis du vil sende din besked uden logfiler, skal du deaktivere denne indstilling." "%1$s crashede sidste gang den blev brugt. Vil du dele en ulykkesrapport med os?" + "Hvis du har problemer med notifikationer, kan upload af notifikationsindstillingerne hjælpe os med at identificere den grundlæggende årsag." + "Send notifikationsindstillinger" "Se logfiler" diff --git a/features/rageshake/impl/src/main/res/values-de/translations.xml b/features/rageshake/impl/src/main/res/values-de/translations.xml index 85893afa54..33baf29d59 100644 --- a/features/rageshake/impl/src/main/res/values-de/translations.xml +++ b/features/rageshake/impl/src/main/res/values-de/translations.xml @@ -14,5 +14,7 @@ "Bildschirmfoto senden" "Die Protokolle werden deiner Nachricht beigefügt, um sicherzustellen, dass alles ordnungsgemäß funktioniert. Um deine Nachricht ohne Protokolle zu senden, deaktiviere diese Einstellung." "%1$s ist bei der letzten Nutzung abgestürzt. Möchtest du einen Absturzbericht mit uns teilen?" + "Wenn du Probleme mit Benachrichtigungen hast, kann das Hochladen der Einstellungen für Benachrichtigungen uns helfen, die Ursache zu finden." + "Einstellungen für Benachrichtigungen senden" "Logs ansehen" diff --git a/features/rageshake/impl/src/main/res/values-et/translations.xml b/features/rageshake/impl/src/main/res/values-et/translations.xml index 64e0f9b307..2f55a9b6b5 100644 --- a/features/rageshake/impl/src/main/res/values-et/translations.xml +++ b/features/rageshake/impl/src/main/res/values-et/translations.xml @@ -14,5 +14,7 @@ "Saada ekraanitõmmis" "Tõhusama veaotsingu nimel lisame sinu veateatele logid. Kui sa seda ei soovi, siis lülita antud valik välja." "%1$s jooksis kokku viimati, kui seda kasutasid. Kas tahaksid selle kohta meile veateate saata?" + "Kui sul teavitused ei toimi päris korralikult, siis teavituste seadistuste üleslaadimine võib aidata meil põhjuse tuvastada." + "Teavituste seadistuste saatmine" "Vaata logisid" diff --git a/features/rageshake/impl/src/main/res/values-fi/translations.xml b/features/rageshake/impl/src/main/res/values-fi/translations.xml index 38f30d4289..1970d538fd 100644 --- a/features/rageshake/impl/src/main/res/values-fi/translations.xml +++ b/features/rageshake/impl/src/main/res/values-fi/translations.xml @@ -14,5 +14,7 @@ "Lähetä kuvakaappaus" "Lähetä lokitiedostot viestisi kanssa, jotta voimme varmistaa, että kaikki toimii oikein. Jos haluat lähettää viestisi ilman lokeja, jätä tämä asetus valitsematta." "%1$s kaatui edellisellä käyttökerralla. Haluatko jakaa virheraportin kanssamme?" + "Jos sinulla on ongelmia ilmoitusten kanssa, ilmoitusasetusten lähettäminen voi auttaa meitä selvittämään ongelman syyn." + "Lähetä ilmoitusasetukset" "Näytä lokitiedostot" diff --git a/features/rageshake/impl/src/main/res/values-fr/translations.xml b/features/rageshake/impl/src/main/res/values-fr/translations.xml index 0d67e42db0..6685903146 100644 --- a/features/rageshake/impl/src/main/res/values-fr/translations.xml +++ b/features/rageshake/impl/src/main/res/values-fr/translations.xml @@ -14,5 +14,7 @@ "Envoyer une capture d’écran" "Pour vérifier que les choses fonctionnent comme prévu, des journaux techniques seront envoyés avec votre message. Pour ne pas envoyer ces journaux, désactivez ce paramètre." "%1$s s’est arrêté la dernière fois qu’il a été utilisé. Souhaitez-vous partager un rapport d’incident avec nous ?" + "Si vous rencontrez des problèmes avec les notifications, l’envoie des paramètres de notification peut nous aider à identifier la cause du problème." + "Envoyer les paramètres de notification" "Afficher les journaux" diff --git a/features/rageshake/impl/src/main/res/values-hu/translations.xml b/features/rageshake/impl/src/main/res/values-hu/translations.xml index f6fcbaa1c9..851d3f0067 100644 --- a/features/rageshake/impl/src/main/res/values-hu/translations.xml +++ b/features/rageshake/impl/src/main/res/values-hu/translations.xml @@ -2,9 +2,9 @@ "Képernyőkép mellékelése" "Felveheti velem a kapcsolatot, ha bármilyen további kérdése van." - "Kapcsolat" + "Kapcsolatfelvétel" "Képernyőkép szerkesztése" - "Írja le a hibát. Mit csinált? Mire számított, hogy mi fog történni? Mi történt valójában? Fogalmazzon a lehető legrészletesebben." + "Írja le a hibát. Mit csinált? Mire számított, hogy történni fog? Mi történt valójában? Fogalmazzon a lehető legrészletesebben." "Írja le a problémát…" "Ha lehetséges, a leírást angolul írja meg." "A leírás túl rövid, adjon meg további részleteket a történtekről. Köszönjük!" @@ -14,5 +14,7 @@ "Képernyőkép küldése" "A naplók szerepelni fognak az üzenetben, hogy megbizonyosodhassunk arról, hogy minden megfelelően működik-e. Ha naplók nélkül szeretné elküldeni az üzenetet, akkor kapcsolja ki ezt a beállítást." "Az %1$s összeomlott a legutóbbi használata óta. Megosztja velünk az összeomlás-jelentést?" + "Ha problémákat tapasztal az értesítésekkel, az értesítési beállítások feltöltése segíthet meghatároznunk a kiváltó okát." + "Értesítési beállítások küldése" "Naplók megtekintése" diff --git a/features/rageshake/impl/src/main/res/values-nb/translations.xml b/features/rageshake/impl/src/main/res/values-nb/translations.xml index 277effef92..4fa4d0a653 100644 --- a/features/rageshake/impl/src/main/res/values-nb/translations.xml +++ b/features/rageshake/impl/src/main/res/values-nb/translations.xml @@ -14,5 +14,6 @@ "Send skjermbilde" "Logger vil bli inkludert i meldingen din, for å sikre at alt fungerer som det skal. For å sende meldingen uten logger, slå av denne innstillingen." "%1$s krasjet sist gang den ble brukt. Vil du dele en krasjrapport med oss?" + "Hvis du har problemer med varsler, kan det å laste opp varslingsinnstillingene hjelpe oss med å finne den underliggende årsaken." "Vis logger" diff --git a/features/rageshake/impl/src/main/res/values-pt/translations.xml b/features/rageshake/impl/src/main/res/values-pt/translations.xml index d2e637db56..a5b44ea109 100644 --- a/features/rageshake/impl/src/main/res/values-pt/translations.xml +++ b/features/rageshake/impl/src/main/res/values-pt/translations.xml @@ -14,5 +14,7 @@ "Enviar captura de ecrã" "Os registos serão incluídos na tua mensagem para garantir que tudo está a funcionar corretamente. Para enviares a tua mensagem sem registos, desativa esta definição." "A %1$s teve uma falha da última vez que foi utilizada. Gostarias de partilhar um relatório de acidente connosco?" + "Se estiveres a ter problemas com as notificações, enviar as configurações pode ajudar-nos a identificar a causa." + "Enviar configurações de notificação" "Ver registos" diff --git a/features/rageshake/impl/src/main/res/values-ro/translations.xml b/features/rageshake/impl/src/main/res/values-ro/translations.xml index 6c2a7cb611..f342ffd4c5 100644 --- a/features/rageshake/impl/src/main/res/values-ro/translations.xml +++ b/features/rageshake/impl/src/main/res/values-ro/translations.xml @@ -14,5 +14,7 @@ "Trimiteți captură de ecran" "Pentru a verifica că lucrurile funcționează conform așteptărilor, log-uri vor fi trimise împreună cu mesajul. Acestea vor fi private. Pentru a trimite doar mesajul, dezactivați această setare." "%1$s s-a blocat ultima dată când a fost folosit. Doriți să ne trimiteți un raport?" + "Dacă întâmpinați probleme cu notificările, încărcarea setărilor notificărilor ne poate ajuta să identificăm cauza principală." + "Trimiteți setările notificărilor" "Vizualizați log-urile" diff --git a/features/rageshake/impl/src/main/res/values-ru/translations.xml b/features/rageshake/impl/src/main/res/values-ru/translations.xml index 5a8f2ceb71..f1d5216405 100644 --- a/features/rageshake/impl/src/main/res/values-ru/translations.xml +++ b/features/rageshake/impl/src/main/res/values-ru/translations.xml @@ -14,5 +14,7 @@ "Отправить снимок экрана" "Чтобы убедиться, что все работает правильно, в сообщение будут включены журналы. Чтобы отправить сообщение без журналов, отключите эту настройку." "При последнем использовании %1$s произошел сбой. Хотите поделиться отчетом о сбое?" + "Если у вас возникли проблемы с уведомлениями, загрузка настроек уведомлений может помочь нам определить основную причину." + "Настройки отправки уведомлений" "Просмотр журналов" diff --git a/features/rageshake/impl/src/main/res/values-zh/translations.xml b/features/rageshake/impl/src/main/res/values-zh/translations.xml index 80125d638a..527a35cdcc 100644 --- a/features/rageshake/impl/src/main/res/values-zh/translations.xml +++ b/features/rageshake/impl/src/main/res/values-zh/translations.xml @@ -14,5 +14,7 @@ "发送屏幕截图" "为确认一切正常运行,您的消息中将包含日志。如要发送不带日志的消息,请关闭此设置。" "%1$s 上次使用时崩溃了。想和我们分享崩溃报告吗?" + "如果您遇到通知问题,上传通知设置可以帮助我们查明根本原因。" + "发送通知设置" "查看日志" diff --git a/features/rageshake/impl/src/main/res/values/localazy.xml b/features/rageshake/impl/src/main/res/values/localazy.xml index 9c18d37a3b..f6d93c4114 100644 --- a/features/rageshake/impl/src/main/res/values/localazy.xml +++ b/features/rageshake/impl/src/main/res/values/localazy.xml @@ -14,5 +14,7 @@ "Send screenshot" "Logs will be included with your message to make sure that everything is working properly. To send your message without logs, turn off this setting." "%1$s crashed the last time it was used. Would you like to share a crash report with us?" + "If you are having issues with notifications, uploading the notification settings can help us pinpoint the root cause." + "Send notification settings" "View logs" diff --git a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomNode.kt b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomNode.kt index 917b081a3d..cd136efcba 100644 --- a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomNode.kt +++ b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomNode.kt @@ -13,7 +13,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs @@ -21,7 +21,7 @@ import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class ReportRoomNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 f47ab19004..42ab1cf08a 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 @@ -17,7 +17,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runUpdatingState @@ -25,7 +25,7 @@ import io.element.android.libraries.matrix.api.core.RoomId import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -@Inject +@AssistedInject class ReportRoomPresenter( @Assisted private val roomId: RoomId, private val reportRoom: ReportRoom, diff --git a/features/reportroom/impl/src/main/res/values-fr/translations.xml b/features/reportroom/impl/src/main/res/values-fr/translations.xml index 51376e34ea..4936a4d54f 100644 --- a/features/reportroom/impl/src/main/res/values-fr/translations.xml +++ b/features/reportroom/impl/src/main/res/values-fr/translations.xml @@ -3,6 +3,6 @@ "Votre rapport a été envoyé avec succès, mais nous avons rencontré un problème en essayant de quitter le salon. Veuillez réessayer." "Impossible de quitter le salon" "Signaler ce salon à votre admin. Si les messages sont chiffrés, votre admin ne pourra pas les lire." - "Décrivez la raison…" + "Décrivez la raison du signalement…" "Signaler le salon" diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt index 274ccef7c8..d7b3242def 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt @@ -14,7 +14,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint import io.element.android.libraries.architecture.inputs @@ -22,7 +22,7 @@ import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class RoomAliasResolverNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 9b65d3e14a..c8a8d18fbc 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 @@ -14,7 +14,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState @@ -25,7 +25,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlin.jvm.optionals.getOrElse -@Inject +@AssistedInject class RoomAliasResolverPresenter( @Assisted private val roomAlias: RoomAlias, private val matrixClient: MatrixClient, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index c883613203..2b4bf10d67 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -21,7 +21,7 @@ import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.Interaction import io.element.android.annotations.ContributesNode import io.element.android.appconfig.LearnMoreConfig @@ -67,7 +67,7 @@ import kotlinx.coroutines.withContext import kotlinx.parcelize.Parcelize @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class RoomDetailsFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt index 6eb8b81d34..2ada71dbc8 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt @@ -20,7 +20,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode import io.element.android.features.leaveroom.api.LeaveRoomRenderer @@ -36,7 +36,7 @@ import timber.log.Timber import io.element.android.libraries.androidutils.R as AndroidUtilsR @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class RoomDetailsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt index 0071aced5d..4408deab1e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt @@ -51,7 +51,7 @@ import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analyticsproviders.api.trackers.captureInteraction -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -143,12 +143,12 @@ class RoomDetailsPresenter( } RoomDetailsEvent.MuteNotification -> { scope.launch(dispatchers.io) { - client.notificationSettingsService().muteRoom(room.roomId) + notificationSettingsService.muteRoom(room.roomId) } } RoomDetailsEvent.UnmuteNotification -> { scope.launch(dispatchers.io) { - client.notificationSettingsService().unmuteRoom(room.roomId, isEncrypted, room.isOneToOne) + notificationSettingsService.unmuteRoom(room.roomId, isEncrypted, room.isOneToOne) } } is RoomDetailsEvent.SetFavorite -> scope.setFavorite(event.isFavorite) @@ -194,7 +194,7 @@ class RoomDetailsPresenter( isFavorite = isFavorite, displayRolesAndPermissionsSettings = !isDm && isUserAdmin, isPublic = joinRule == JoinRule.Public, - heroes = roomInfo.heroes.toPersistentList(), + heroes = roomInfo.heroes.toImmutableList(), pinnedMessagesCount = pinnedMessagesCount, snackbarMessage = snackbarMessage, canShowKnockRequests = canShowKnockRequests, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt index 70810ee46b..43566b86a8 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt @@ -18,7 +18,7 @@ import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomNotificationSettings import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList data class RoomDetailsState( val roomId: RoomId, @@ -59,7 +59,7 @@ data class RoomDetailsState( if (isPublic) { add(RoomBadge.PUBLIC) } - }.toPersistentList() + }.toImmutableList() } @Immutable diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt index c9087d6458..96d19c5ecf 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt @@ -27,7 +27,7 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.RoomNotificationSettings import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUserList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList open class RoomDetailsStateProvider : PreviewParameterProvider { override val values: Sequence @@ -135,7 +135,7 @@ fun aRoomDetailsState( isFavorite = isFavorite, displayRolesAndPermissionsSettings = displayAdminSettings, isPublic = isPublic, - heroes = heroes.toPersistentList(), + heroes = heroes.toImmutableList(), pinnedMessagesCount = pinnedMessagesCount, snackbarMessage = snackbarMessage, canShowKnockRequests = canShowKnockRequests, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index 61d5ba115a..3f20985ab8 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -89,7 +89,6 @@ import io.element.android.services.analytics.compose.LocalAnalyticsService import io.element.android.services.analyticsproviders.api.trackers.captureInteraction import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toPersistentList @Composable fun RoomDetailsView( @@ -400,7 +399,7 @@ private fun RoomHeaderSection( avatarType = AvatarType.Room( heroes = heroes.map { user -> user.getAvatarData(size = AvatarSize.RoomDetailsHeader) - }.toPersistentList(), + }.toImmutableList(), isTombstoned = isTombstoned, ), contentDescription = avatarUrl?.let { stringResource(CommonStrings.a11y_room_avatar) }, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditNode.kt index f04a50c657..8143f1848f 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditNode.kt @@ -14,14 +14,14 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.RoomScope import io.element.android.services.analytics.api.AnalyticsService @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class RoomDetailsEditNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 04b98e3d98..3c3acc7b4d 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 @@ -15,7 +15,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +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.InvitePeoplePresenter @@ -25,7 +25,7 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.services.analytics.api.AnalyticsService @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class RoomInviteMembersNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 178410d264..cc7a6e2151 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 @@ -15,7 +15,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +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 @@ -26,7 +26,7 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.services.analytics.api.AnalyticsService @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class RoomMemberListNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 c5bd507d69..69efd04b9e 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 @@ -38,7 +38,7 @@ import io.element.android.libraries.matrix.ui.room.roomMemberIdentityStateChange import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toPersistentMap +import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first @@ -69,10 +69,10 @@ class RoomMemberListPresenter( val canInvite by room.canInviteAsState(syncUpdateFlow.value) val roomModerationState = roomMembersModerationPresenter.present() - val roomMemberIdentityStates by produceState(persistentMapOf()) { + val roomMemberIdentityStates by produceState(persistentMapOf()) { room.roomMemberIdentityStateChange(waitForEncryption = true) .onEach { identities -> - value = identities.associateBy({ it.identityRoomMember.userId }, { it.identityState }).toPersistentMap() + value = identities.associateBy({ it.identityRoomMember.userId }, { it.identityState }).toImmutableMap() } .launchIn(this) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt index 35d386976f..3b364c6f92 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt @@ -15,7 +15,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode import io.element.android.features.userprofile.shared.UserProfileNodeHelper @@ -29,7 +29,7 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder import io.element.android.services.analytics.api.AnalyticsService @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class RoomMemberDetailsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 61c1eb9019..d5cf9e85cb 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 @@ -15,7 +15,7 @@ import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfilePresenterFactory import io.element.android.features.userprofile.api.UserProfileState @@ -41,7 +41,7 @@ import kotlinx.coroutines.launch * Presenter for room member details screen. * Rely on UserProfilePresenter, but override some fields with room member info when available. */ -@Inject +@AssistedInject class RoomMemberDetailsPresenter( @Assisted private val roomMemberId: UserId, private val room: JoinedRoom, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsNode.kt index 7ef6ea9c1c..0e5a9b23b1 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsNode.kt @@ -15,7 +15,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs @@ -24,7 +24,7 @@ import io.element.android.libraries.di.RoomScope import io.element.android.services.analytics.api.AnalyticsService @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class RoomNotificationSettingsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt index 93e22a9d9e..413e71169a 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt @@ -19,7 +19,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter @@ -37,7 +37,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlin.time.Duration.Companion.seconds -@Inject +@AssistedInject class RoomNotificationSettingsPresenter( private val room: JoinedRoom, private val notificationSettingsService: NotificationSettingsService, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsFlowNode.kt index 61bb0e99a3..62689489eb 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsFlowNode.kt @@ -18,7 +18,7 @@ import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType @@ -33,7 +33,7 @@ import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class RolesAndPermissionsFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt index 230e83e660..a430b0f6a5 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt @@ -16,7 +16,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.room.BaseRoom @@ -29,7 +29,7 @@ import kotlinx.coroutines.flow.take import kotlinx.coroutines.launch @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class RolesAndPermissionsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsNode.kt index 906c97fb2e..cebcc56e7f 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsNode.kt @@ -14,7 +14,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs @@ -22,7 +22,7 @@ import io.element.android.libraries.di.RoomScope import kotlinx.parcelize.Parcelize @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class ChangeRoomPermissionsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsPresenter.kt index f010fd6057..ec90df1d5e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsPresenter.kt @@ -17,7 +17,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.features.roomdetails.impl.analytics.trackPermissionChangeAnalytics import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter @@ -29,7 +29,7 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -@Inject +@AssistedInject class ChangeRoomPermissionsPresenter( @Assisted private val section: ChangeRoomPermissionsSection, private val room: JoinedRoom, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStateProvider.kt index 7af850230e..07d3455a90 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStateProvider.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList class ChangeRoomPermissionsStateProvider : PreviewParameterProvider { override val values: Sequence @@ -45,7 +45,7 @@ internal fun aChangeRoomPermissionsState( ) = ChangeRoomPermissionsState( section = section, currentPermissions = currentPermissions, - items = items.toPersistentList(), + items = items.toImmutableList(), hasChanges = hasChanges, saveAction = saveAction, confirmExitAction = confirmExitAction, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyFlowNode.kt index 2744834c73..a0295dde36 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyFlowNode.kt @@ -15,7 +15,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.navmodel.backstack.BackStack import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.roomdetails.impl.securityandprivacy.editroomaddress.EditRoomAddressNode import io.element.android.libraries.architecture.BackstackView @@ -25,7 +25,7 @@ import io.element.android.libraries.di.RoomScope import kotlinx.parcelize.Parcelize @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class SecurityAndPrivacyFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyNode.kt index ffbf58af1a..15580aab6a 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyNode.kt @@ -14,12 +14,12 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.RoomScope @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class SecurityAndPrivacyNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenter.kt index a2a55aed68..abc0ff72af 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenter.kt @@ -19,7 +19,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.features.roomdetails.impl.securityandprivacy.editroomaddress.matchesServer import io.element.android.features.roomdetails.impl.securityandprivacy.permissions.securityAndPrivacyPermissionsAsState import io.element.android.libraries.architecture.AsyncAction @@ -40,7 +40,7 @@ import kotlinx.coroutines.awaitAll import kotlinx.coroutines.delay import kotlinx.coroutines.launch -@Inject +@AssistedInject class SecurityAndPrivacyPresenter( @Assisted private val navigator: SecurityAndPrivacyNavigator, private val matrixClient: MatrixClient, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/editroomaddress/EditRoomAddressNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/editroomaddress/EditRoomAddressNode.kt index 943400d4c3..76cb1311fc 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/editroomaddress/EditRoomAddressNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/editroomaddress/EditRoomAddressNode.kt @@ -14,13 +14,13 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyNavigator import io.element.android.libraries.di.RoomScope @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class EditRoomAddressNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/editroomaddress/EditRoomAddressPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/editroomaddress/EditRoomAddressPresenter.kt index db02c60346..95aee73c13 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/editroomaddress/EditRoomAddressPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/editroomaddress/EditRoomAddressPresenter.kt @@ -18,7 +18,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyNavigator import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter @@ -34,7 +34,7 @@ import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidityEf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -@Inject +@AssistedInject class EditRoomAddressPresenter( @Assisted private val navigator: SecurityAndPrivacyNavigator, private val client: MatrixClient, diff --git a/features/roomdetails/impl/src/main/res/values-be/translations.xml b/features/roomdetails/impl/src/main/res/values-be/translations.xml index 01c076fdb5..8ab30212b7 100644 --- a/features/roomdetails/impl/src/main/res/values-be/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-be/translations.xml @@ -51,6 +51,7 @@ "Стандартныя" "Апавяшчэнні" "Замацаваныя паведамленні" + "Профіль" "Ролі і дазволы" "Назва пакоя" "Бяспека" diff --git a/features/roomdetails/impl/src/main/res/values-bg/translations.xml b/features/roomdetails/impl/src/main/res/values-bg/translations.xml index fb01acb4fb..cfe719f190 100644 --- a/features/roomdetails/impl/src/main/res/values-bg/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-bg/translations.xml @@ -1,5 +1,6 @@ + "Възникна грешка при обновяването на настройките за известия." "Вашият сървър не поддържа тази опция в шифровани стаи, може да не получавате известия в някои стаи." "Анкети" "Само администратори" @@ -26,9 +27,13 @@ "Без шифроване" "Общодостъпна стая" "Редактиране на стаята" + "Възникна неизвестна грешка и информацията не можа да бъде променена." "Не може да се обнови стаята" "Съобщенията са защитени с ключове. Само вие и получателите имате уникалните ключове, за да ги отключите." "Шифроването на съобщенията е включено" + "Възникна грешка при зареждането на настройките за известия." + "Неуспешно заглушаване на тази стая, моля, опитайте отново." + "Неуспешно раззаглушаване на тази стая, моля, опитайте отново." "Поканване на хора" "Напускане на разговора" "Напускане на стаята" @@ -40,6 +45,7 @@ "Профил" "Роли и разрешения" "Име на стаята" + "Защита и поверителност" "Защита" "Споделяне на стаята" "Информация за стаята" @@ -53,7 +59,16 @@ "Администратор" "Модератор" "Членове на стаята" + "Разрешаване на персонализирана настройка" + "Включването на това ще замени вашата настройка по подразбиране" "Да бъда известяван в този чат за" + "Можете да го промените във вашите %1$s." + "глобални настройки" + "Настройка по подразбиране" + "Премахване на персонализираната настройка" + "Възникна грешка при зареждането на настройките за известия." + "Неуспешно възстановяване на режима по подразбиране, моля, опитайте отново." + "Неуспешно задаване на режима, моля, опитайте отново." "Всички съобщения" "Само споменавания и ключови думи" "В тази стая, да бъда известяван за" @@ -67,9 +82,25 @@ "Роли" "Подробности за стаята" "Роли и разрешения" + "Добавяне на адрес на стаята" + "Да, включване на шифроването" + "Да се включи ли шифроването?" + "Веднъж включено, шифроването не може да бъде изключено." "Шифроване" + "Включване на шифроване от край до край" + "Всеки може да намери и да се присъедини" "Всеки" + "Хората могат да се присъединят само ако са поканени" + "Само с покана" + "Достъп до стаята" + "Пространствата в момента не се поддържат" + "Членове на пространството" + "Ще ви е необходим адрес на стаята, за да я направите видима в директорията на стаите." "Видима в директорията на обществените стаи" "Всеки" + "Кой може да чете историята" + "Само за членове откакто са поканени" + "Само за членове от избирането на тази опция" "Видимост на стаята" + "Защита и поверителност" diff --git a/features/roomdetails/impl/src/main/res/values-da/translations.xml b/features/roomdetails/impl/src/main/res/values-da/translations.xml index 29733beeed..77e34d87dc 100644 --- a/features/roomdetails/impl/src/main/res/values-da/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-da/translations.xml @@ -50,6 +50,8 @@ "Der opstod en fejl under indlæsning af notifikationsindstillinger." "Det lykkedes ikke at slå lyden fra for dette rum. Prøv igen." "Det lykkedes ikke at slå lyden til igen i dette rum. Prøv igen." + "Luk ikke appen, før den er færdig." + "Forbereder invitationer…" "Invitér andre" "Forlad samtalen" "Forlad rum" diff --git a/features/roomdetails/impl/src/main/res/values-fr/translations.xml b/features/roomdetails/impl/src/main/res/values-fr/translations.xml index feea21919d..c30bb2c8c9 100644 --- a/features/roomdetails/impl/src/main/res/values-fr/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-fr/translations.xml @@ -50,6 +50,7 @@ "Une erreur s’est produite lors du chargement des paramètres de notification." "Échec de la mise en sourdine de ce salon, veuillez réessayer." "Échec de la désactivation de la mise en sourdine de ce salon, veuillez réessayer." + "Ne fermez pas l’application avant que l’opération soit terminée." "Préparation des invitations…" "Inviter des amis" "Quitter la discussion" diff --git a/features/roomdetails/impl/src/main/res/values-hu/translations.xml b/features/roomdetails/impl/src/main/res/values-hu/translations.xml index 6e2484fc68..941389ca42 100644 --- a/features/roomdetails/impl/src/main/res/values-hu/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-hu/translations.xml @@ -50,6 +50,8 @@ "Hiba történt az értesítési beállítások betöltésekor." "Nem sikerült elnémítani ezt a szobát, próbálja újra." "Nem sikerült feloldani a szoba némítását, próbálja újra." + "Ne zárja be az alkalmazást, amíg nem végzett." + "Meghívók előkészítése…" "Ismerősök meghívása" "Beszélgetés elhagyása" "Szoba elhagyása" diff --git a/features/roomdetails/impl/src/main/res/values-ka/translations.xml b/features/roomdetails/impl/src/main/res/values-ka/translations.xml index 1beb70d4dd..481082bf63 100644 --- a/features/roomdetails/impl/src/main/res/values-ka/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ka/translations.xml @@ -46,6 +46,7 @@ "მორგებული" "ნაგულისხმევი" "შეტყობინებები" + "პროფილი" "როლები და ნებართვები" "ოთახის სახელი" "უსაფრთხოება" diff --git a/features/roomdetails/impl/src/main/res/values-nb/translations.xml b/features/roomdetails/impl/src/main/res/values-nb/translations.xml index 596b387b04..16042fdf57 100644 --- a/features/roomdetails/impl/src/main/res/values-nb/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-nb/translations.xml @@ -22,6 +22,7 @@ "Rediger administratorer" "Du vil ikke kunne angre denne handlingen. Du forfremmer brukeren til å ha samme rettighetsnivå som deg." "Legg til administrator?" + "Du kan ikke angre denne handlingen. Du overfører eierskapet til de valgte brukerne. Når du forlater siden, vil dette være permanent." "Overføre eierskapet?" "Degradere" "Du vil ikke kunne angre denne endringen ettersom du degraderer deg selv, og hvis du er den siste privilegerte brukeren i rommet, vil det være umulig å få tilbake privilegiene." @@ -49,6 +50,8 @@ "Det oppstod en feil ved lasting av varslingsinnstillinger." "Mislyktes i å dempe dette rommet, prøv igjen." "Mislyktes i å oppheve dempingen av dette rommet, prøv igjen." + "Ikke lukk appen før den er ferdig." + "Forbereder invitasjoner…" "Inviter folk" "Forlat samtalen" "Forlat rommet" diff --git a/features/roomdetails/impl/src/main/res/values-nl/translations.xml b/features/roomdetails/impl/src/main/res/values-nl/translations.xml index 9b932f60fe..baec0b0bb9 100644 --- a/features/roomdetails/impl/src/main/res/values-nl/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-nl/translations.xml @@ -51,6 +51,7 @@ "Standaard" "Meldingen" "Vastgezette berichten" + "Profiel" "Rollen en rechten" "Naam van de kamer" "Beveiliging" diff --git a/features/roomdetails/impl/src/main/res/values-pt/translations.xml b/features/roomdetails/impl/src/main/res/values-pt/translations.xml index cba9ab5ac1..6338f63b0c 100644 --- a/features/roomdetails/impl/src/main/res/values-pt/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-pt/translations.xml @@ -50,6 +50,8 @@ "Erro ao carregar as configurações de notificação." "Não foi possível silenciar esta sala, por favor tenta novamente." "Não foi possível dessilenciar esta sala, por favor tenta novamente." + "Não feches a aplicação até concluir." + "A preparar convites…" "Convidar pessoas" "Sair da conversa" "Sair da sala" diff --git a/features/roomdetails/impl/src/main/res/values-ro/translations.xml b/features/roomdetails/impl/src/main/res/values-ro/translations.xml index fbdf3132a4..7379a45550 100644 --- a/features/roomdetails/impl/src/main/res/values-ro/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ro/translations.xml @@ -50,6 +50,8 @@ "A apărut o eroare la încărcarea setărilor pentru notificari." "Dezactivarea notificarilor pentru această cameră a eșuat, încercați din nou." "Activarea notificarilor pentru această cameră a eșuat, încercați din nou." + "Nu închideți aplicația până nu se termină." + "Se pregătesc invitațiile…" "Invitați prieteni" "Părăsiți conversația" "Părăsiți camera" diff --git a/features/roomdetails/impl/src/main/res/values-ru/translations.xml b/features/roomdetails/impl/src/main/res/values-ru/translations.xml index e24adbd6c1..82efd71adf 100644 --- a/features/roomdetails/impl/src/main/res/values-ru/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ru/translations.xml @@ -50,6 +50,8 @@ "Произошла ошибка при загрузке настроек уведомлений." "Не удалось отключить звук в этой комнате, попробуйте еще раз." "Не удалось включить звук в эту комнату, попробуйте еще раз." + "Не закрывайте приложение, пока не закончите." + "Подготовка приглашений…" "Пригласить в комнату" "Покинуть беседу" "Покинуть комнату" diff --git a/features/roomdetails/impl/src/main/res/values-ur/translations.xml b/features/roomdetails/impl/src/main/res/values-ur/translations.xml index 12a8d964cb..8cd040835f 100644 --- a/features/roomdetails/impl/src/main/res/values-ur/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ur/translations.xml @@ -51,6 +51,7 @@ "طے شدہ" "اطلاعات" "مثبوتہ پیغامات" + "نمایہ" "کردارہا اور اجازتیں" "کمرے کا نام" "حفاظت" diff --git a/features/roomdetails/impl/src/main/res/values-uz/translations.xml b/features/roomdetails/impl/src/main/res/values-uz/translations.xml index 9ced40ed1f..e9b50e5400 100644 --- a/features/roomdetails/impl/src/main/res/values-uz/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-uz/translations.xml @@ -51,6 +51,7 @@ "Standart" "Bildirishnomalar" "Qadalgan xabarlar" + "Profil" "Rollar va ruxsatlar" "Xona nomi" "Xavfsizlik" diff --git a/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml b/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml index 035de6fa4d..888701406d 100644 --- a/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml @@ -60,7 +60,7 @@ "預設" "通知" "釘選訊息" - "設定檔" + "個人檔案" "請求加入" "身份與權限" "聊天室名稱" diff --git a/features/roomdetails/impl/src/main/res/values-zh/translations.xml b/features/roomdetails/impl/src/main/res/values-zh/translations.xml index 48a92f93a2..b260163ca3 100644 --- a/features/roomdetails/impl/src/main/res/values-zh/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-zh/translations.xml @@ -50,6 +50,8 @@ "加载通知设置时出错。" "无法将此聊天室静音,请重试。" "无法取消此聊天室的静音,请重试。" + "完成之前请勿关闭应用程序。" + "准备邀请…" "邀请朋友" "离开聊天" "离开聊天室" @@ -58,7 +60,7 @@ "默认" "通知" "置顶消息" - "简介" + "个人资料" "申请加入" "角色与权限" "聊天室名称" diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt index bfcfa184cb..695168aa16 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt @@ -104,7 +104,7 @@ class RoomDetailsPresenterTest { client = matrixClient, room = room, featureFlagService = featureFlagService, - notificationSettingsService = matrixClient.notificationSettingsService(), + notificationSettingsService = matrixClient.notificationSettingsService, roomMembersDetailsPresenterFactory = roomMemberDetailsPresenterFactory, leaveRoomPresenter = { leaveRoomState }, roomCallStatePresenter = { aStandByCallState() }, diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryNode.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryNode.kt index 50f50f8d4e..03d2be6e35 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryNode.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryNode.kt @@ -14,14 +14,14 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.roomdirectory.api.RoomDescription import io.element.android.features.roomdirectory.api.RoomDirectoryEntryPoint import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class RoomDirectoryNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStateProvider.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStateProvider.kt index bdebcc3a93..2d4cc75aa9 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStateProvider.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStateProvider.kt @@ -14,7 +14,7 @@ import io.element.android.features.roommembermoderation.api.RoomMemberModeration import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList class InternalRoomMemberModerationStateProvider : PreviewParameterProvider { override val values: Sequence @@ -86,7 +86,7 @@ fun aRoomMembersModerationState( canKick = canKick, canBan = canBan, selectedUser = selectedUser, - actions = actions.toPersistentList(), + actions = actions.toImmutableList(), kickUserAsyncAction = kickUserAsyncAction, banUserAsyncAction = banUserAsyncAction, unbanUserAsyncAction = unbanUserAsyncAction, 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 3d07ee75e1..2ddb3ad34a 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 @@ -35,9 +35,9 @@ import io.element.android.libraries.matrix.ui.room.canBanAsState import io.element.android.libraries.matrix.ui.room.canKickAsState import io.element.android.libraries.matrix.ui.room.userPowerLevelAsState import io.element.android.services.analytics.api.AnalyticsService -import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.drop @@ -68,7 +68,7 @@ class RoomMemberModerationPresenter( var selectedUser by remember { mutableStateOf(null) } - val moderationActions = remember { mutableStateOf(persistentListOf()) } + val moderationActions = remember { mutableStateOf>(persistentListOf()) } fun handleEvent(event: RoomMemberModerationEvents) { when (event) { @@ -149,7 +149,7 @@ class RoomMemberModerationPresenter( canKick: Boolean, canBan: Boolean, currentUserMemberPowerLevel: Long, - ): PersistentList { + ): ImmutableList { return buildList { add(ModerationActionState(action = ModerationAction.DisplayProfile, isEnabled = true)) // Assume the member is a regular user when it's unknown @@ -168,7 +168,7 @@ class RoomMemberModerationPresenter( add(ModerationActionState(action = ModerationAction.BanUser, isEnabled = canModerateThisUser)) } } - }.toPersistentList() + }.toImmutableList() } private fun CoroutineScope.kickUser( diff --git a/features/roommembermoderation/impl/src/main/res/values-be/translations.xml b/features/roommembermoderation/impl/src/main/res/values-be/translations.xml index bd744e96e7..b4b1f35f18 100644 --- a/features/roommembermoderation/impl/src/main/res/values-be/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-be/translations.xml @@ -5,8 +5,11 @@ "Яны не змогуць зноў далучыцца да гэтага пакоя, калі іх запросяць." "Вы ўпэўнены, што хочаце заблакіраваць гэтага карыстальніка?" "Блакіроўка %1$s" + "Яны змогуць зноў далучыцца да гэтага пакоя, калі іх запросяць." "Прагляд профілю" "Выдаліць удзельніка з пакоя" "Выдаліць удзельніка і забараніць далучацца ў будучыні?" "Выдаленне %1$s…" + "Разблакіраваць" + "Разблакіроўка %1$s" diff --git a/features/roommembermoderation/impl/src/main/res/values-cs/translations.xml b/features/roommembermoderation/impl/src/main/res/values-cs/translations.xml index 2c5e51972e..263aa9a2e0 100644 --- a/features/roommembermoderation/impl/src/main/res/values-cs/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-cs/translations.xml @@ -6,7 +6,7 @@ "Jste si jisti, že chcete vykázat tohoto člena?" "Vykazování %1$s" "Odebrat" - "Budou moci znovu vstoupit do této místnosti, pokud budou pozváni." + "Pokud budou pozváni, budou se moci do této místnosti znovu připojit." "Opravdu chcete tohoto člena odebrat?" "Zobrazit profil" "Odebrat z místnosti" diff --git a/features/roommembermoderation/impl/src/main/res/values-cy/translations.xml b/features/roommembermoderation/impl/src/main/res/values-cy/translations.xml index cfa26c6e9b..2d7667aaa7 100644 --- a/features/roommembermoderation/impl/src/main/res/values-cy/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-cy/translations.xml @@ -13,7 +13,7 @@ "Dileu aelod a\'u gwahardd rhag ymuno yn y dyfodol?" "Wrthi\'n dileu %1$s…" "Dad-wahardd o\'r ystafell" - "Dad-wahardd" + "Adfer" "Bydden nhw\'n gallu ymuno â\'r ystafell eto os fydd rhywun yn eu gwahodd" "Ydych chi\'n siŵr eich bod chi eisiau dadwahardd yr aelod hwn?" "Dad-wahardd %1$s" diff --git a/features/roommembermoderation/impl/src/main/res/values-de/translations.xml b/features/roommembermoderation/impl/src/main/res/values-de/translations.xml index d977d05564..6c6f9dc827 100644 --- a/features/roommembermoderation/impl/src/main/res/values-de/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-de/translations.xml @@ -6,7 +6,7 @@ "Möchtest du diesen Nutzer wirklich sperren?" "%1$s wird gesperrt." "Entfernen" - "Sie können diesem Chat auf Einladung wieder beitreten." + "Die Nutzer können dem Chat wieder beitreten, wenn sie eingeladen werden." "Möchtest du dieses Mitglied wirklich entfernen?" "Nutzerprofil anzeigen" "Mitglied entfernen" @@ -16,5 +16,5 @@ "Sperre aufheben" "Sie können dann diesem Chat auf Einladung wieder beitreten." "Möchtest du die Sperre dieses Mitglieds wirklich aufheben?" - "Sperre für %1$s aufheben" + "%1$s wird entsperrt." diff --git a/features/roommembermoderation/impl/src/main/res/values-el/translations.xml b/features/roommembermoderation/impl/src/main/res/values-el/translations.xml index 2ed8f03bc4..89bf97f4d2 100644 --- a/features/roommembermoderation/impl/src/main/res/values-el/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-el/translations.xml @@ -13,7 +13,7 @@ "Αφαίρεση μέλους και απαγόρευση συμμετοχής στο μέλλον;" "Αφαίρεση %1$s…" "Άρση αποκλεισμού από την αίθουσα" - "Άρση αποκλεισμού" + "Αναίρεση αποκλεισμού" "Θα μπορούν να συμμετάσχουν και πάλι στην αίθουσα αν προσκληθούν" "Σίγουρα θες να καταργήσεις τον αποκλεισμό αυτού του μέλους;" "Άρση αποκλεισμού %1$s" diff --git a/features/roommembermoderation/impl/src/main/res/values-es/translations.xml b/features/roommembermoderation/impl/src/main/res/values-es/translations.xml index 04049e9e31..7f83f7546e 100644 --- a/features/roommembermoderation/impl/src/main/res/values-es/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-es/translations.xml @@ -13,7 +13,7 @@ "¿Sacar al miembro y prohibirle unirse en el futuro?" "Eliminando %1$s…" "Eliminar veto en la sala" - "Eliminar veto" + "Quitar veto" "Podría volver a unirse a la sala si se le invita" "¿Seguro que quieres levantarle el veto a este miembro?" "Levantando veto a %1$s" diff --git a/features/roommembermoderation/impl/src/main/res/values-et/translations.xml b/features/roommembermoderation/impl/src/main/res/values-et/translations.xml index ffb1d79f88..1a0a3ec431 100644 --- a/features/roommembermoderation/impl/src/main/res/values-et/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-et/translations.xml @@ -6,7 +6,7 @@ "Kas sa oled kindel, et soovid sellele kasutajale seada suhtluskeelu?" "Seame kasutajale %1$s suhtluskeelu" "Eemalda" - "Uue kutse saamisel on tal võimalik selle jututoaga uuesti liituda." + "Kutse olemasolul saab ta nüüd jututoaga uuesti liituda" "Kas sa oled kindel, et soovid selle osaleja eemaldada?" "Vaata profiili" "Eemalda kasutaja jututoast" diff --git a/features/roommembermoderation/impl/src/main/res/values-eu/translations.xml b/features/roommembermoderation/impl/src/main/res/values-eu/translations.xml index a7f61e1a55..560e0a6b64 100644 --- a/features/roommembermoderation/impl/src/main/res/values-eu/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-eu/translations.xml @@ -9,4 +9,6 @@ "Kendu gelatik" "Kidea kendu eta etorkizunean sartzea debekatu?" "%1$s kentzen…" + "Kendu debekua" + "%1$s(r)i debekua kentzen" diff --git a/features/roommembermoderation/impl/src/main/res/values-fr/translations.xml b/features/roommembermoderation/impl/src/main/res/values-fr/translations.xml index 5a09dddf4e..fa01ddb49a 100644 --- a/features/roommembermoderation/impl/src/main/res/values-fr/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-fr/translations.xml @@ -2,11 +2,11 @@ "Bannir du salon" "Bannir" - "Il ne pourra pas rejoindre le salon à nouveau, même si il est invité." + "Ce compte ne pourra pas rejoindre le salon à nouveau, même si il est invité." "Êtes-vous certain de vouloir bannir ce membre ?" "Bannissement de %1$s" "Retirer" - "Cet utilisateur pourra rejoindre le salon à nouveau si il est invité." + "Il pourra rejoindre le salon à nouveau si il est invité." "Voulez-vous vraiment supprimer ce membre ?" "Voir le profil" "Retirer le membre du salon" @@ -14,7 +14,7 @@ "Enlever %1$s…" "Débannir du salon" "Débannir" - "L’utilisateur pourra à nouveau rejoindre le salon s’il est invité." - "Êtes-vous sûr de vouloir débannir cet utilisateur?" + "Ce compte pourra à nouveau rejoindre le salon s’il est invité." + "Êtes-vous sûr de vouloir débannir ce compte?" "Débannissement de %1$s" diff --git a/features/roommembermoderation/impl/src/main/res/values-hu/translations.xml b/features/roommembermoderation/impl/src/main/res/values-hu/translations.xml index db36f41964..4c68806e7d 100644 --- a/features/roommembermoderation/impl/src/main/res/values-hu/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-hu/translations.xml @@ -13,8 +13,8 @@ "Eltávolítja a tagot, és megtiltja a jövőbeni csatlakozást?" "%1$s eltávolítása…" "Visszaengedés a szobába" - "Kitiltás visszavonása" + "Tiltás feloldása" "Újra beléphetnek a szobába, ha meghívják őket." "Biztos, hogy feloldja a felhasználó kitiltását?" - "%1$s kitiltásának feloldása" + "%1$s tiltásának feloldása" diff --git a/features/roommembermoderation/impl/src/main/res/values-in/translations.xml b/features/roommembermoderation/impl/src/main/res/values-in/translations.xml index 778f68402c..bddbdb0a67 100644 --- a/features/roommembermoderation/impl/src/main/res/values-in/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-in/translations.xml @@ -6,7 +6,7 @@ "Apakah Anda yakin ingin mencekal anggota ini?" "Mencekal %1$s" "Hapus" - "Pengguna dapat bergabung dalam ruangan ini lagi jika diundang." + "Pengguna dapat bergabung ke ruangan ini lagi jika diundang." "Apakah Anda yakin ingin menghapus anggota ini?" "Tampilkan profil" "Keluarkan dari ruangan" diff --git a/features/roommembermoderation/impl/src/main/res/values-it/translations.xml b/features/roommembermoderation/impl/src/main/res/values-it/translations.xml index 41b8baa473..7791d04546 100644 --- a/features/roommembermoderation/impl/src/main/res/values-it/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-it/translations.xml @@ -13,8 +13,8 @@ "Rimuovere e vietare l\'accesso in futuro?" "Rimozione di %1$s…" "Riammetti nella stanza" - "Togli ban" + "Riammetti" "Potranno unirsi di nuovo alla stanza se invitati" "Sei sicuro di voler sbloccare questo membro?" - "Rimuovendo il ban di %1$s" + "Riammissione di %1$s" diff --git a/features/roommembermoderation/impl/src/main/res/values-ka/translations.xml b/features/roommembermoderation/impl/src/main/res/values-ka/translations.xml index a2eb6d4bad..adfdde605c 100644 --- a/features/roommembermoderation/impl/src/main/res/values-ka/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-ka/translations.xml @@ -5,8 +5,11 @@ "მოწვევის შემთხვევაში ამ ოთახში კვლავ გაწევრიანებას ვერ შეძლებენ." "დარწმუნებული ხართ, რომ ამ წევრის დაბლოკვა გსურთ?" "%1$s-ს დაბლოკვა" + "მოწვევის შემთხვევაში განბლოკილი მომხმარებელი ისევ შეძლებს ოთახს შეუერთდეს." "პროფილის ნახვა" "ოთახიდან გაგდება" "გსურთ წევრის გაგდება და მომავალში გაწევრიანების აკრძალვა?" "%1$s-ს გაგდება…" + "განბლოკვა" + "%1$s-ს განბლოკვა" diff --git a/features/roommembermoderation/impl/src/main/res/values-ko/translations.xml b/features/roommembermoderation/impl/src/main/res/values-ko/translations.xml index 4831c177cc..0675c39f2f 100644 --- a/features/roommembermoderation/impl/src/main/res/values-ko/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-ko/translations.xml @@ -6,14 +6,14 @@ "정말로 이 회원을 차단하시겠습니까?" "차단 %1$s" "제거" - "초대되면 이 방에 다시 참여할 수 있습니다." + "초대받으면 이 방에 다시 들어올 수 있습니다." "이 회원을 정말로 제거하시겠습니까?" "프로필 보기" "방에서 제거" "회원을 삭제하고 앞으로 가입을 금지하시겠습니까?" "%1$s 제거 중…" "방에서 차단 해제" - "차단 해제" + "금지 해제" "초대되면 다시 방에 참여할 수 있습니다." "이 회원을 정말로 차단해제 하시겠습니까?" "차단 해제 %1$s" diff --git a/features/roommembermoderation/impl/src/main/res/values-nb/translations.xml b/features/roommembermoderation/impl/src/main/res/values-nb/translations.xml index 323526170b..4507fce49c 100644 --- a/features/roommembermoderation/impl/src/main/res/values-nb/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-nb/translations.xml @@ -13,7 +13,7 @@ "Fjerne medlem og utestenge fra å bli med i fremtiden?" "Fjerner %1$s…" "Fjern utestengelsen fra rommet" - "Opphev utestengelsen" + "Opphev utestengelse" "De vil kunne bli med i rommet igjen hvis de blir invitert" "Er du sikker på at du vil oppheve utestengelsen av dette medlemmet?" "Oppheve utestengelsen av %1$s" diff --git a/features/roommembermoderation/impl/src/main/res/values-nl/translations.xml b/features/roommembermoderation/impl/src/main/res/values-nl/translations.xml index 64317b83bf..55aca0fbb9 100644 --- a/features/roommembermoderation/impl/src/main/res/values-nl/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-nl/translations.xml @@ -5,8 +5,11 @@ "Ze kunnen niet meer toetreden tot deze kamer als ze worden uitgenodigd." "Weet je zeker dat je dit lid wilt verbannen?" "%1$s verbannen" + "Ze kunnen opnieuw tot de kamer toetreden als ze worden uitgenodigd." "Profiel bekijken" "Verwijderen uit kamer" "Lid verwijderen en toekomstige deelname verbieden?" "%1$s wordt verwijderd…" + "Ontbannen" + "%1$s ontbannen" diff --git a/features/roommembermoderation/impl/src/main/res/values-pl/translations.xml b/features/roommembermoderation/impl/src/main/res/values-pl/translations.xml index b94e3f8d8b..20246af787 100644 --- a/features/roommembermoderation/impl/src/main/res/values-pl/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-pl/translations.xml @@ -6,7 +6,7 @@ "Czy na pewno chcesz zbanować tego członka?" "Banowanie %1$s" "Usuń" - "Będą mogli ponownie dołączyć do pokoju, jeśli zostaną zaproszeni." + "Będą mogli ponownie dołączyć do tego pokoju, jeśli zostaną zaproszeni." "Czy na pewno chcesz usunąć tego członka?" "Wyświetl profil" "Usuń z pokoju" @@ -16,5 +16,5 @@ "Odbanuj" "Mogą ponownie dołączyć do pokoju, po otrzymaniu zaproszenia" "Czy na pewno chcesz odbanować tego członka?" - "Odbanowuję %1$s" + "Odbanowanie %1$s" diff --git a/features/roommembermoderation/impl/src/main/res/values-pt-rBR/translations.xml b/features/roommembermoderation/impl/src/main/res/values-pt-rBR/translations.xml index 803eda1f80..4cbc0ba081 100644 --- a/features/roommembermoderation/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-pt-rBR/translations.xml @@ -6,7 +6,7 @@ "Tem certeza de que quer banir este membro?" "Banindo %1$s" "Remover" - "Essa pessoa poderá entrar na sala novamente se for convidada." + "Esta pessoa poderá entrar nesta sala novamente se for convidada." "Tem certeza de que deseja remover este membro?" "Ver perfil" "Remover da sala" diff --git a/features/roommembermoderation/impl/src/main/res/values-pt/translations.xml b/features/roommembermoderation/impl/src/main/res/values-pt/translations.xml index 42ceede746..3989c4de7b 100644 --- a/features/roommembermoderation/impl/src/main/res/values-pt/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-pt/translations.xml @@ -6,15 +6,15 @@ "Tens a certeza que queres banir este participante?" "A banir %1$s" "Remover" - "Poderão entrar na sala novamente se convidados." + "Poderão juntar-se novamente a esta sala se forem convidados." "Tens certeza que queres remover este membro?" "Ver perfil" "Remover da sala" "Remover participante e proibir que entre no futuro?" "A remover %1$s…" "Desbanir da sala" - "Desbanir" + "Anular banimento" "Eles poderão entrar novamente na sala se forem convidados" "Tens certeza que queres desbanir este membro?" - "Desbanindo %1$s" + "A anular banimento de %1$s" diff --git a/features/roommembermoderation/impl/src/main/res/values-ro/translations.xml b/features/roommembermoderation/impl/src/main/res/values-ro/translations.xml index fa05933234..4811b5263f 100644 --- a/features/roommembermoderation/impl/src/main/res/values-ro/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-ro/translations.xml @@ -6,15 +6,15 @@ "Sunteți sigur că doriți să interziceți acest membru?" "Se interzice %1$s" "Îndepărtați" - "Se vor putea alătura din nou acestei camere dacă sunt invitați." + "Se vor putea alătura din nou acestei săli dacă sunt invitați." "Sunteți sigur că doriți să îndepărtați acest membru?" "Vizualizare profil" "Înlăturați membrul" "Înlăturați membrul și interziceți-i să se alăture în viitor?" "Se îndepărtează %1$s" "Revocati excluderea din camera" - "Anulați excluderea" + "Anulare excludere" "Aceștia se vor putea alătura din nou camerei dacă sunt invitați." "Sunteți sigur că doriți să dezactivați excluderea impusă acestui membru?" - "Se anulează excluderea lui %1$s" + "Se anulează interzicerea lui %1$s" diff --git a/features/roommembermoderation/impl/src/main/res/values-sk/translations.xml b/features/roommembermoderation/impl/src/main/res/values-sk/translations.xml index df5214c17c..ffdd634b0b 100644 --- a/features/roommembermoderation/impl/src/main/res/values-sk/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-sk/translations.xml @@ -16,5 +16,5 @@ "Zrušiť zákaz" "V prípade pozvania by sa mohli opäť pripojiť k miestnosti" "Naozaj chcete zrušiť zablokovanie tohto člena?" - "Zrušenie zákazu pre %1$s" + "Zrušenie zákazu %1$s" diff --git a/features/roommembermoderation/impl/src/main/res/values-tr/translations.xml b/features/roommembermoderation/impl/src/main/res/values-tr/translations.xml index 73f17edd02..a63e4eb55d 100644 --- a/features/roommembermoderation/impl/src/main/res/values-tr/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-tr/translations.xml @@ -5,8 +5,11 @@ "Davet edilseler bile bu odaya tekrar katılamazlar." "Bu üyeyi yasaklamak istediğinize emin misiniz?" "Yasaklanıyor %1$s" + "Davet edildikleri takdirde bu odaya tekrar katılabileceklerdir." "Profili görüntüle" "Odadan çıkar" "Üyeyi çıkarın ve gelecekte katılmasını yasaklayın?" "Kaldırılıyor %1$s…" + "Yasağı Kaldır" + "Yasak kaldırılıyor %1$s" diff --git a/features/roommembermoderation/impl/src/main/res/values-ur/translations.xml b/features/roommembermoderation/impl/src/main/res/values-ur/translations.xml index 333c86f056..2ea1262c4e 100644 --- a/features/roommembermoderation/impl/src/main/res/values-ur/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-ur/translations.xml @@ -5,8 +5,11 @@ "اگر وہ مدعو کیا گیا تو وہ دوبارہ اس کمرے میں شامل نہیں ہوسکیں گے۔" "کیا آپ کو یقین ہے کہ آپ اس رکن کو محظور کرنا چاہتے ہیں؟" "%1$s کو محظور کر رہا ہے" + "اگر وہ مدعو کیا جائیں تو وہ دوبارہ اس کمرے میں شامل ہوسکیں گے۔" "نمایہ ملاحظہ کریں" "کمرے سے ہٹائیں" "رکن کو ہٹائیں اور مستقبل میں شمولیت پر پابندی لگائیں؟" "%1$s کو ہٹا رہا ہے…" + "غیر محظور کریں" + "%1$s کو غیر محظور کر رہا ہے" diff --git a/features/roommembermoderation/impl/src/main/res/values-uz/translations.xml b/features/roommembermoderation/impl/src/main/res/values-uz/translations.xml index 2b08fe58a0..d7b09f0e8d 100644 --- a/features/roommembermoderation/impl/src/main/res/values-uz/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-uz/translations.xml @@ -5,8 +5,11 @@ "Taklif qilingan taqdirda ham, ular bu xonaga boshqa qo‘shila olmaydilar." "Haqiqatan ham bu aʼzoni taqiqlamoqchimisiz?" "Taqiqlash %1$s" + "Agar taklif qilinsa, ular bu xonaga qayta qo‘shilishlari mumkin." "Profilni koʻrish" "Xonadan olib tashlash" "Aʻzo oʻchirilsinmi va kelgusida qoʻshilish taqiqlansinmi?" "Oʻchirish %1$s …" + "Taqiqni bekor qilish" + "Taqiqni bekor qilish %1$s" diff --git a/features/roommembermoderation/impl/src/main/res/values-zh-rTW/translations.xml b/features/roommembermoderation/impl/src/main/res/values-zh-rTW/translations.xml index 1f2e372b8c..de8a9712ee 100644 --- a/features/roommembermoderation/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-zh-rTW/translations.xml @@ -6,15 +6,15 @@ "您確定要將此成員加入黑名單?" "正在將 %1$s 加入黑名單" "移除" - "若收到邀請,他們可以再次加入此聊天室。" + "如果收到邀請,他們能再次加入聊天室。" "您真的想要移除此成員嗎?" "查看個人檔案" "踢出聊天室" "移除成員並禁止未來再度加入?" "正在踢出 %1$s…" "從聊天室解除封鎖" - "解除封鎖" + "解除黑名單" "若受到邀請,他們仍可再次加入聊天室" "您確定您想要取消封鎖此成員嗎?" - "解除封鎖 %1$s" + "正在解除黑名單 %1$s" diff --git a/features/roommembermoderation/impl/src/main/res/values-zh/translations.xml b/features/roommembermoderation/impl/src/main/res/values-zh/translations.xml index 54a0978da1..a27d2ded05 100644 --- a/features/roommembermoderation/impl/src/main/res/values-zh/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-zh/translations.xml @@ -13,7 +13,7 @@ "删除成员并禁止重新加入?" "正在移除 %1$s……" "从房间取消解封" - "解除封禁" + "取消封禁" "如果再次收到邀请,他们可以重新加入该聊天室" "确定要解除该成员的封禁吗?" "解除封禁 %1$s" 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 3b647c6478..40e09b3620 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 @@ -30,7 +30,7 @@ import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.test import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -360,7 +360,7 @@ class RoomMemberModerationPresenterTest { updateMembersResult = { Result.success(Unit) } ), ).apply { - val roomMembers = listOfNotNull(targetRoomMember).toPersistentList() + val roomMembers = listOfNotNull(targetRoomMember).toImmutableList() givenRoomMembersState(state = RoomMembersState.Ready(roomMembers)) } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt index b0cfafd915..4d79f8ea1b 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt @@ -18,7 +18,7 @@ import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.securebackup.api.SecureBackupEntryPoint import io.element.android.features.securebackup.impl.disable.SecureBackupDisableNode @@ -34,7 +34,7 @@ import io.element.android.libraries.di.SessionScope import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class SecureBackupFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableNode.kt index 023fcd8f17..8af4f3e613 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableNode.kt @@ -13,12 +13,12 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class SecureBackupDisableNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt index 14b2109506..77d1fe8f32 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt @@ -14,12 +14,12 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class SecureBackupEnterRecoveryKeyNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManager.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManager.kt index fe6a86a357..4ffaa9c344 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManager.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManager.kt @@ -10,7 +10,7 @@ package io.element.android.features.securebackup.impl.reset import dev.zacsweers.metro.Inject import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.di.annotations.SessionCoroutineScope -import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.IdentityResetHandle import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus @@ -24,7 +24,7 @@ import kotlinx.coroutines.launch @Inject class ResetIdentityFlowManager( - private val matrixClient: MatrixClient, + private val encryptionService: EncryptionService, @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, private val sessionVerificationService: SessionVerificationService, ) { @@ -46,7 +46,7 @@ class ResetIdentityFlowManager( resetHandleFlow.value = AsyncData.Loading() sessionCoroutineScope.launch { - matrixClient.encryptionService().startIdentityReset() + encryptionService.startIdentityReset() .onSuccess { handle -> resetHandleFlow.value = AsyncData.Success(handle) } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt index 8abe8e5ffb..dfc9425ebe 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt @@ -24,7 +24,7 @@ import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.compound.theme.ElementTheme import io.element.android.features.securebackup.impl.reset.password.ResetIdentityPasswordNode @@ -47,7 +47,7 @@ import kotlinx.parcelize.Parcelize import timber.log.Timber @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class ResetIdentityFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordNode.kt index dd4218d344..3c22673bff 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordNode.kt @@ -13,7 +13,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs @@ -22,7 +22,7 @@ import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.encryption.IdentityPasswordResetHandle @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class ResetIdentityPasswordNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootNode.kt index b6a0883eb7..8267242f97 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootNode.kt @@ -13,12 +13,12 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class ResetIdentityRootNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt index a7b22a64e3..6d4db197d3 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt @@ -16,13 +16,13 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.appconfig.LearnMoreConfig import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class SecureBackupRootNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupNode.kt index 322546bc56..6adcb890ae 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupNode.kt @@ -13,7 +13,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.securebackup.impl.R import io.element.android.libraries.architecture.NodeInputs @@ -23,7 +23,7 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class SecureBackupSetupNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 35dd8356da..58a6c4b43c 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 @@ -20,7 +20,7 @@ import com.freeletics.flowredux.compose.StateAndDispatch import com.freeletics.flowredux.compose.rememberStateAndDispatch import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.features.securebackup.impl.loggerTagSetup import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyUserStory import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyViewState @@ -32,7 +32,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch import timber.log.Timber -@Inject +@AssistedInject class SecureBackupSetupPresenter( @Assisted private val isChangeRecoveryKeyUserStory: Boolean, private val stateMachine: SecureBackupSetupStateMachine, diff --git a/features/securebackup/impl/src/main/res/values-eo/translations.xml b/features/securebackup/impl/src/main/res/values-eo/translations.xml index e3ea61ec1c..4b8e1833a3 100644 --- a/features/securebackup/impl/src/main/res/values-eo/translations.xml +++ b/features/securebackup/impl/src/main/res/values-eo/translations.xml @@ -23,9 +23,11 @@ "Generate a new backup password" "Backup password changed" "Change backup password?" + "Create new backup password" "Please try again to confirm access to your message backup." "Incorrect backup password" "You might have seen the terms \"recovery key\", \"security key\" or \"security phrase\" instead of \"backup password\". Don\'t worry, this is all the same." + "Lost your backup password?" "Backup password confirmed" "Enter your backup password" "Copied backup password" diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManagerTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManagerTest.kt index a26d6fabef..702b10a1bf 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManagerTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManagerTest.kt @@ -12,7 +12,6 @@ import com.google.common.truth.Truth.assertThat import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.encryption.IdentityResetHandle import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus -import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.libraries.matrix.test.encryption.FakeIdentityPasswordResetHandle import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService @@ -130,10 +129,9 @@ class ResetIdentityFlowManagerTest { private fun TestScope.createFlowManager( encryptionService: FakeEncryptionService = FakeEncryptionService(), - client: FakeMatrixClient = FakeMatrixClient(encryptionService = encryptionService), sessionVerificationService: FakeSessionVerificationService = FakeSessionVerificationService(), ) = ResetIdentityFlowManager( - matrixClient = client, + encryptionService = encryptionService, sessionCoroutineScope = this, sessionVerificationService = sessionVerificationService, ) diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareNode.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareNode.kt index 08b6448ac4..e268419920 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareNode.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareNode.kt @@ -19,7 +19,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.ParentNode import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.share.api.ShareEntryPoint import io.element.android.libraries.architecture.NodeInputs @@ -31,7 +31,7 @@ import io.element.android.libraries.roomselect.api.RoomSelectMode import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class ShareNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 056d8b912e..d3222edf5e 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 @@ -13,7 +13,7 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState @@ -31,7 +31,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlin.coroutines.cancellation.CancellationException -@Inject +@AssistedInject class SharePresenter( @Assisted private val intent: Intent, @SessionCoroutineScope diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutNode.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutNode.kt index b98a38f29d..1bacc78932 100644 --- a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutNode.kt +++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutNode.kt @@ -14,14 +14,14 @@ 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.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs import io.element.android.libraries.matrix.api.core.SessionId @ContributesNode(AppScope::class) -@Inject +@AssistedInject class SignedOutNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, @@ -32,7 +32,7 @@ class SignedOutNode( ) : NodeInputs private val inputs: Inputs = inputs() - private val presenter = presenterFactory.create(inputs.sessionId.value) + private val presenter = presenterFactory.create(inputs.sessionId) @Composable override fun View(modifier: Modifier) { 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 9810892ff3..7c9d1e6d83 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 @@ -9,44 +9,43 @@ package io.element.android.features.signedout.impl import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.sessionstorage.api.SessionStore +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -@Inject +@AssistedInject class SignedOutPresenter( - // Cannot inject SessionId - @Assisted private val sessionId: String, + @Assisted private val sessionId: SessionId, private val sessionStore: SessionStore, private val buildMeta: BuildMeta, ) : Presenter { @AssistedFactory fun interface Factory { - fun create(sessionId: String): SignedOutPresenter + fun create(sessionId: SessionId): SignedOutPresenter } @Composable override fun present(): SignedOutState { - val sessions by remember { - sessionStore.sessionsFlow() - }.collectAsState(initial = emptyList()) val signedOutSession by remember { - derivedStateOf { sessions.firstOrNull { it.userId == sessionId } } - } + sessionStore.sessionsFlow().map { sessions -> + sessions.firstOrNull { it.userId == sessionId.value } + } + }.collectAsState(initial = null) val coroutineScope = rememberCoroutineScope() fun handleEvents(event: SignedOutEvents) { when (event) { SignedOutEvents.SignInAgain -> coroutineScope.launch { - sessionStore.removeSession(sessionId) + sessionStore.removeSession(sessionId.value) } } } diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutStateProvider.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutStateProvider.kt index 55e29c9e26..a7b95a8537 100644 --- a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutStateProvider.kt +++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutStateProvider.kt @@ -43,5 +43,9 @@ private fun aSessionData( passphrase = null, sessionPath = "/a/path/to/a/session", cachePath = "/a/path/to/a/cache", + position = 0, + lastUsageIndex = 0, + userDisplayName = null, + userAvatarUrl = null, ) } diff --git a/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/DefaultSignedOutEntryPointTest.kt b/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/DefaultSignedOutEntryPointTest.kt index 860ad88d8a..6366ba5ea1 100644 --- a/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/DefaultSignedOutEntryPointTest.kt +++ b/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/DefaultSignedOutEntryPointTest.kt @@ -28,7 +28,7 @@ class DefaultSignedOutEntryPointTest { buildContext = buildContext, plugins = plugins, presenterFactory = { sessionId -> - assertThat(sessionId).isEqualTo(A_SESSION_ID.value) + assertThat(sessionId).isEqualTo(A_SESSION_ID) createSignedOutPresenter() } ) 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 de5634f269..e53c0af112 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 @@ -70,7 +70,7 @@ internal fun createSignedOutPresenter( sessionStore: SessionStore = InMemorySessionStore(), ): SignedOutPresenter { return SignedOutPresenter( - sessionId = sessionId.value, + sessionId = sessionId, sessionStore = sessionStore, buildMeta = aBuildMeta(applicationName = AN_APPLICATION_NAME), ) diff --git a/features/space/api/src/main/kotlin/io/element/android/features/space/api/SpaceEntryPoint.kt b/features/space/api/src/main/kotlin/io/element/android/features/space/api/SpaceEntryPoint.kt index 5f7be8dba0..bdea93f3ef 100644 --- a/features/space/api/src/main/kotlin/io/element/android/features/space/api/SpaceEntryPoint.kt +++ b/features/space/api/src/main/kotlin/io/element/android/features/space/api/SpaceEntryPoint.kt @@ -31,6 +31,6 @@ interface SpaceEntryPoint : FeatureEntryPoint { ) : NodeInputs interface Callback : Plugin { - fun onOpenRoom(roomId: RoomId) + fun onOpenRoom(roomId: RoomId, viaParameters: List) } } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPoint.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPoint.kt index 1ef8275b27..8591978417 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPoint.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPoint.kt @@ -33,7 +33,7 @@ class DefaultSpaceEntryPoint : SpaceEntryPoint { } override fun build(): Node { - return parentNode.createNode(buildContext, plugins = plugins.toList()) + return parentNode.createNode(buildContext, plugins = plugins.toList()) } } } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt new file mode 100644 index 0000000000..78472c7b31 --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2025 New Vector 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.features.space.impl + +import android.os.Parcelable +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.lifecycle.subscribe +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import com.bumble.appyx.navmodel.backstack.BackStack +import com.bumble.appyx.navmodel.backstack.operation.push +import dev.zacsweers.metro.Assisted +import dev.zacsweers.metro.AssistedInject +import io.element.android.annotations.ContributesNode +import io.element.android.features.space.api.SpaceEntryPoint +import io.element.android.features.space.impl.di.SpaceFlowGraph +import io.element.android.features.space.impl.leave.LeaveSpaceNode +import io.element.android.features.space.impl.root.SpaceNode +import io.element.android.libraries.architecture.BackstackView +import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.architecture.inputs +import io.element.android.libraries.di.DependencyInjectionGraphOwner +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.spaces.SpaceService +import kotlinx.parcelize.Parcelize + +@ContributesNode(SessionScope::class) +@AssistedInject +class SpaceFlowNode( + @Assisted val buildContext: BuildContext, + @Assisted plugins: List, + spaceService: SpaceService, + graphFactory: SpaceFlowGraph.Factory, +) : BaseFlowNode( + backstack = BackStack( + initialElement = NavTarget.Root, + savedStateMap = buildContext.savedStateMap, + ), + buildContext = buildContext, + plugins = plugins, +), DependencyInjectionGraphOwner { + private val inputs: SpaceEntryPoint.Inputs = inputs() + private val callback = plugins.filterIsInstance().single() + private val spaceRoomList = spaceService.spaceRoomList(inputs.roomId) + override val graph = graphFactory.create(spaceRoomList) + + sealed interface NavTarget : Parcelable { + @Parcelize + data object Root : NavTarget + + @Parcelize + data object Leave : NavTarget + } + + override fun onBuilt() { + super.onBuilt() + lifecycle.subscribe( + onDestroy = { + spaceRoomList.destroy() + } + ) + } + + override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { + return when (navTarget) { + NavTarget.Leave -> { + createNode(buildContext, listOf(inputs)) + } + NavTarget.Root -> { + val callback = object : SpaceNode.Callback { + override fun onOpenRoom(roomId: RoomId, viaParameters: List) { + callback.onOpenRoom(roomId, viaParameters) + } + + override fun onLeaveSpace() { + backstack.push(NavTarget.Leave) + } + } + createNode(buildContext, listOf(inputs, callback)) + } + } + } + + @Composable + override fun View(modifier: Modifier) = BackstackView() +} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpacePresenter.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpacePresenter.kt deleted file mode 100644 index 1955ab652e..0000000000 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpacePresenter.kt +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2025 New Vector 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.space.impl - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject -import io.element.android.features.invite.api.SeenInvitesStore -import io.element.android.features.space.api.SpaceEntryPoint -import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.core.coroutine.mapState -import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.api.spaces.SpaceRoomList -import io.element.android.libraries.matrix.ui.safety.rememberHideInvitesAvatar -import kotlinx.collections.immutable.persistentSetOf -import kotlinx.collections.immutable.toPersistentList -import kotlinx.collections.immutable.toPersistentSet -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch -import java.util.Optional -import kotlin.jvm.optionals.getOrNull - -@Inject -class SpacePresenter( - @Assisted private val inputs: SpaceEntryPoint.Inputs, - private val client: MatrixClient, - private val seenInvitesStore: SeenInvitesStore, -) : Presenter { - @AssistedFactory - fun interface Factory { - fun create(inputs: SpaceEntryPoint.Inputs): SpacePresenter - } - - private val spaceRoomList = client.spaceService.spaceRoomList(inputs.roomId) - - @Composable - override fun present(): SpaceState { - LaunchedEffect(Unit) { - paginate() - } - val hideInvitesAvatar by client.rememberHideInvitesAvatar() - val seenSpaceInvites by remember { - seenInvitesStore.seenRoomIds().map { it.toPersistentSet() } - }.collectAsState(persistentSetOf()) - - val coroutineScope = rememberCoroutineScope() - val children by spaceRoomList.spaceRoomsFlow.collectAsState(emptyList()) - val hasMoreToLoad by remember { - spaceRoomList.paginationStatusFlow.mapState { status -> - when (status) { - is SpaceRoomList.PaginationStatus.Idle -> status.hasMoreToLoad - SpaceRoomList.PaginationStatus.Loading -> true - } - } - }.collectAsState() - - val currentSpace by remember { spaceRoomList.currentSpaceFlow() }.collectAsState(Optional.empty()) - - fun handleEvents(event: SpaceEvents) { - when (event) { - SpaceEvents.LoadMore -> coroutineScope.paginate() - } - } - return SpaceState( - currentSpace = currentSpace.getOrNull(), - children = children.toPersistentList(), - seenSpaceInvites = seenSpaceInvites, - hideInvitesAvatar = hideInvitesAvatar, - hasMoreToLoad = hasMoreToLoad, - eventSink = ::handleEvents, - ) - } - - private fun CoroutineScope.paginate() = launch { - spaceRoomList.paginate() - } -} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceState.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceState.kt deleted file mode 100644 index ad822283ca..0000000000 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceState.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2025 New Vector 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.space.impl - -import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.spaces.SpaceRoom -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.ImmutableSet - -data class SpaceState( - val currentSpace: SpaceRoom?, - val children: ImmutableList, - val seenSpaceInvites: ImmutableSet, - val hideInvitesAvatar: Boolean, - val hasMoreToLoad: Boolean, - val eventSink: (SpaceEvents) -> Unit -) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceStateProvider.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceStateProvider.kt deleted file mode 100644 index cf2fcf92b5..0000000000 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceStateProvider.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2025 New Vector 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.space.impl - -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.spaces.SpaceRoom -import io.element.android.libraries.previewutils.room.aSpaceRoom -import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toImmutableSet - -open class SpaceStateProvider : PreviewParameterProvider { - override val values: Sequence - get() = sequenceOf( - aSpaceState(), - aSpaceState( - parentSpace = aSpaceRoom( - name = null, - numJoinedMembers = 5, - childrenCount = 10, - worldReadable = true, - ), - hasMoreToLoad = true, - ), - aSpaceState( - hasMoreToLoad = true, - children = aListOfSpaceRooms(), - ), - aSpaceState( - hasMoreToLoad = false, - children = aListOfSpaceRooms() - ) - // Add other states here - ) -} - -fun aSpaceState( - parentSpace: SpaceRoom? = aSpaceRoom( - numJoinedMembers = 5, - childrenCount = 10, - worldReadable = true, - roomId = RoomId("!spaceId0:example.com"), - ), - children: List = emptyList(), - seenSpaceInvites: Set = emptySet(), - hideInvitesAvatar: Boolean = false, - hasMoreToLoad: Boolean = false, -) = SpaceState( - currentSpace = parentSpace, - children = children.toImmutableList(), - seenSpaceInvites = seenSpaceInvites.toImmutableSet(), - hideInvitesAvatar = hideInvitesAvatar, - hasMoreToLoad = hasMoreToLoad, - eventSink = {} -) - -private fun aListOfSpaceRooms(): List { - return listOf( - aSpaceRoom(roomId = RoomId("!spaceId0:example.com")), - aSpaceRoom(roomId = RoomId("!spaceId1:example.com")), - aSpaceRoom(roomId = RoomId("!spaceId2:example.com")), - ) -} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceView.kt deleted file mode 100644 index 53aa61a0c7..0000000000 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceView.kt +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2025 New Vector 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.space.impl - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.rememberUpdatedState -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.heading -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.text.font.FontStyle -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.dp -import io.element.android.compound.theme.ElementTheme -import io.element.android.libraries.designsystem.components.avatar.Avatar -import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.designsystem.components.avatar.AvatarType -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.CircularProgressIndicator -import io.element.android.libraries.designsystem.theme.components.Scaffold -import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.room.CurrentUserMembership -import io.element.android.libraries.matrix.api.spaces.SpaceRoom -import io.element.android.libraries.matrix.ui.components.SpaceHeaderView -import io.element.android.libraries.matrix.ui.components.SpaceRoomItemView -import io.element.android.libraries.matrix.ui.model.getAvatarData -import io.element.android.libraries.ui.strings.CommonStrings -import kotlinx.collections.immutable.toImmutableList - -@Composable -fun SpaceView( - state: SpaceState, - onBackClick: () -> Unit, - onRoomClick: (roomId: RoomId) -> Unit, - modifier: Modifier = Modifier, -) { - Scaffold( - modifier = modifier, - topBar = { - SpaceViewTopBar(currentSpace = state.currentSpace, onBackClick = onBackClick) - }, - content = { padding -> - Box( - modifier = Modifier.padding(padding) - ) { - SpaceViewContent( - state = state, - onRoomClick = onRoomClick - ) - } - }, - ) -} - -@Composable -private fun SpaceViewContent( - state: SpaceState, - onRoomClick: (roomId: RoomId) -> Unit, - modifier: Modifier = Modifier, -) { - LazyColumn(modifier.fillMaxSize()) { - val currentSpace = state.currentSpace - if (currentSpace != null) { - item { - SpaceHeaderView( - avatarData = currentSpace.getAvatarData(AvatarSize.SpaceHeader), - name = currentSpace.name, - topic = currentSpace.topic, - joinRule = currentSpace.joinRule, - heroes = currentSpace.heroes.toImmutableList(), - numberOfMembers = currentSpace.numJoinedMembers, - numberOfRooms = currentSpace.childrenCount, - ) - } - } - state.children.forEach { spaceRoom -> - item { - val isInvitation = spaceRoom.state == CurrentUserMembership.INVITED - SpaceRoomItemView( - spaceRoom = spaceRoom, - showUnreadIndicator = isInvitation && spaceRoom.roomId !in state.seenSpaceInvites, - hideAvatars = isInvitation && state.hideInvitesAvatar, - onClick = { - onRoomClick(spaceRoom.roomId) - }, - onLongClick = { - // TODO - } - ) - } - } - if (state.hasMoreToLoad) { - item { - LoadingMoreIndicator(eventSink = state.eventSink) - } - } - } -} - -@Composable -private fun LoadingMoreIndicator( - eventSink: (SpaceEvents) -> Unit, - modifier: Modifier = Modifier -) { - Box( - modifier = modifier.fillMaxWidth(), - contentAlignment = Alignment.Center, - ) { - CircularProgressIndicator( - strokeWidth = 2.dp, - modifier = Modifier.padding(vertical = 8.dp) - ) - val latestEventSink by rememberUpdatedState(eventSink) - LaunchedEffect(Unit) { - latestEventSink(SpaceEvents.LoadMore) - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun SpaceViewTopBar( - currentSpace: SpaceRoom?, - onBackClick: () -> Unit, - modifier: Modifier = Modifier, -) { - TopAppBar( - modifier = modifier, - navigationIcon = { - BackButton(onClick = onBackClick) - }, - title = { - if (currentSpace != null) { - SpaceAvatarAndNameRow( - name = currentSpace.name, - avatarData = currentSpace.getAvatarData(AvatarSize.TimelineRoom), - ) - } - }, - actions = { - }, - ) -} - -@Composable -private fun SpaceAvatarAndNameRow( - name: String?, - avatarData: AvatarData, - modifier: Modifier = Modifier -) { - Row( - modifier = modifier, - verticalAlignment = Alignment.CenterVertically - ) { - Avatar( - avatarData = avatarData, - avatarType = AvatarType.Space(), - ) - Text( - modifier = Modifier - .padding(horizontal = 8.dp) - .semantics { - heading() - }, - text = name ?: stringResource(CommonStrings.common_no_space_name), - style = ElementTheme.typography.fontBodyLgMedium, - fontStyle = FontStyle.Italic.takeIf { name == null }, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - } -} - -@PreviewsDayNight -@Composable -internal fun SpaceViewPreview( - @PreviewParameter(SpaceStateProvider::class) state: SpaceState -) = ElementPreview { - SpaceView( - state = state, - onRoomClick = {}, - onBackClick = {}, - ) -} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/di/SpaceFlowGraph.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/di/SpaceFlowGraph.kt new file mode 100644 index 0000000000..b1dac522b4 --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/di/SpaceFlowGraph.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2024 New Vector 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.space.impl.di + +import dev.zacsweers.metro.ContributesTo +import dev.zacsweers.metro.GraphExtension +import dev.zacsweers.metro.Provides +import io.element.android.libraries.architecture.NodeFactoriesBindings +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.spaces.SpaceRoomList + +@GraphExtension(SpaceFlowScope::class) +interface SpaceFlowGraph : NodeFactoriesBindings { + @ContributesTo(SessionScope::class) + @GraphExtension.Factory + interface Factory { + fun create(@Provides spaceRoomList: SpaceRoomList): SpaceFlowGraph + } +} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/di/SpaceFlowScope.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/di/SpaceFlowScope.kt new file mode 100644 index 0000000000..77fb07f871 --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/di/SpaceFlowScope.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2024 New Vector 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.space.impl.di + +abstract class SpaceFlowScope private constructor() 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/LeaveSpaceEvents.kt new file mode 100644 index 0000000000..558ae8454d --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceEvents.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2025 New Vector 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.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 +} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceNode.kt new file mode 100644 index 0000000000..c60bddea1d --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceNode.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2025 New Vector 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.space.impl.leave + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.lifecycle.subscribe +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.Assisted +import dev.zacsweers.metro.AssistedInject +import io.element.android.annotations.ContributesNode +import io.element.android.features.space.api.SpaceEntryPoint +import io.element.android.features.space.impl.di.SpaceFlowScope +import io.element.android.libraries.architecture.inputs +import io.element.android.libraries.matrix.api.MatrixClient + +@ContributesNode(SpaceFlowScope::class) +@AssistedInject +class LeaveSpaceNode( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + matrixClient: MatrixClient, + presenterFactory: LeaveSpacePresenter.Factory, +) : Node(buildContext, plugins = plugins) { + private val inputs: SpaceEntryPoint.Inputs = inputs() + private val leaveSpaceHandle = matrixClient.spaceService.getLeaveSpaceHandle(inputs.roomId) + private val presenter: LeaveSpacePresenter = presenterFactory.create(leaveSpaceHandle) + + override fun onBuilt() { + super.onBuilt() + lifecycle.subscribe( + onDestroy = { + leaveSpaceHandle.close() + } + ) + } + + @Composable + override fun View(modifier: Modifier) { + val state = presenter.present() + LeaveSpaceView( + state = state, + onCancel = ::navigateUp, + modifier = modifier + ) + } +} 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 new file mode 100644 index 0000000000..19e593bf7c --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenter.kt @@ -0,0 +1,149 @@ +/* + * Copyright 2025 New Vector 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.space.impl.leave + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import dev.zacsweers.metro.Assisted +import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.map +import io.element.android.libraries.architecture.runUpdatingState +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.spaces.LeaveSpaceHandle +import io.element.android.libraries.matrix.api.spaces.LeaveSpaceRoom +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentSetOf +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +@AssistedInject +class LeaveSpacePresenter( + @Assisted private val leaveSpaceHandle: LeaveSpaceHandle, +) : Presenter { + @AssistedFactory + fun interface Factory { + fun create(leaveSpaceHandle: LeaveSpaceHandle): LeaveSpacePresenter + } + + data class LeaveSpaceRooms( + val current: LeaveSpaceRoom?, + val others: List, + ) + + @Composable + override fun present(): LeaveSpaceState { + val coroutineScope = rememberCoroutineScope() + var retryCount by remember { mutableIntStateOf(0) } + val leaveSpaceAction = remember { + mutableStateOf>(AsyncAction.Uninitialized) + } + var selectedRoomIds by remember { + mutableStateOf>(setOf()) + } + var leaveSpaceRooms by remember { + mutableStateOf>(AsyncData.Loading()) + } + LaunchedEffect(retryCount) { + val rooms = leaveSpaceHandle.rooms() + val (currentRoom, otherRooms) = rooms.getOrNull() + .orEmpty() + .partition { it.spaceRoom.roomId == leaveSpaceHandle.id } + // By default select all rooms that can be left + val otherRoomsExcludingDm = otherRooms.filter { it.spaceRoom.isDirect != true } + selectedRoomIds = otherRoomsExcludingDm + .filter { it.isLastAdmin.not() } + .map { it.spaceRoom.roomId } + leaveSpaceRooms = rooms.fold( + onSuccess = { + AsyncData.Success( + LeaveSpaceRooms( + current = currentRoom.firstOrNull(), + others = otherRoomsExcludingDm.toImmutableList(), + ) + ) + }, + onFailure = { AsyncData.Failure(it) } + ) + } + var selectableSpaceRooms by remember { + mutableStateOf>>(AsyncData.Loading()) + } + LaunchedEffect(selectedRoomIds, leaveSpaceRooms) { + selectableSpaceRooms = leaveSpaceRooms.map { + it?.others.orEmpty().map { room -> + SelectableSpaceRoom( + spaceRoom = room.spaceRoom, + isLastAdmin = room.isLastAdmin, + isSelected = selectedRoomIds.contains(room.spaceRoom.roomId), + ) + }.toImmutableList() + } + } + + fun handleEvents(event: LeaveSpaceEvents) { + when (event) { + LeaveSpaceEvents.Retry -> { + leaveSpaceRooms = AsyncData.Loading() + retryCount += 1 + } + LeaveSpaceEvents.DeselectAllRooms -> { + selectedRoomIds = persistentSetOf() + } + LeaveSpaceEvents.SelectAllRooms -> { + selectedRoomIds = selectableSpaceRooms.dataOrNull() + .orEmpty() + .filter { it.isLastAdmin.not() } + .map { it.spaceRoom.roomId } + } + is LeaveSpaceEvents.ToggleRoomSelection -> { + selectedRoomIds = if (selectedRoomIds.contains(event.roomId)) { + selectedRoomIds - event.roomId + } else { + selectedRoomIds + event.roomId + } + } + LeaveSpaceEvents.LeaveSpace -> coroutineScope.leaveSpace( + leaveSpaceAction = leaveSpaceAction, + selectedRoomIds = selectedRoomIds, + ) + LeaveSpaceEvents.CloseError -> { + leaveSpaceAction.value = AsyncAction.Uninitialized + } + } + } + + return LeaveSpaceState( + spaceName = leaveSpaceRooms.dataOrNull()?.current?.spaceRoom?.displayName, + isLastAdmin = leaveSpaceRooms.dataOrNull()?.current?.isLastAdmin == true, + selectableSpaceRooms = selectableSpaceRooms, + leaveSpaceAction = leaveSpaceAction.value, + eventSink = ::handleEvents, + ) + } + + private fun CoroutineScope.leaveSpace( + leaveSpaceAction: MutableState>, + selectedRoomIds: Collection, + ) = launch { + runUpdatingState(leaveSpaceAction) { + leaveSpaceHandle.leave(selectedRoomIds.toList()) + } + } +} 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 new file mode 100644 index 0000000000..0f2a0f93f6 --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceState.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2025 New Vector 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.space.impl.leave + +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.architecture.AsyncData +import kotlinx.collections.immutable.ImmutableList + +data class LeaveSpaceState( + val spaceName: String?, + val isLastAdmin: Boolean, + val selectableSpaceRooms: AsyncData>, + val leaveSpaceAction: AsyncAction, + val eventSink: (LeaveSpaceEvents) -> Unit, +) { + private val rooms = selectableSpaceRooms.dataOrNull().orEmpty() + private val partition = rooms.partition { it.isLastAdmin } + private val lastAdminRooms = partition.first + private val selectableRooms = partition.second + + /** + * True if we should show the quick action to select/deselect all rooms. + */ + val showQuickAction = isLastAdmin.not() && selectableRooms.isNotEmpty() + + /** + * True if we should show the leave button. + */ + val showLeaveButton = isLastAdmin.not() && selectableSpaceRooms is AsyncData.Success + + /** + * True if there all the selectable rooms are selected. + */ + val areAllSelected = selectableRooms.all { it.isSelected } + + /** + * True if there are rooms but the user is the last admin in all of them. + */ + val hasOnlyLastAdminRoom = lastAdminRooms.isNotEmpty() && selectableRooms.isEmpty() + + /** + * Number of selected rooms. + */ + val selectedRoomsCount = selectableRooms.count { it.isSelected } +} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceStateProvider.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceStateProvider.kt new file mode 100644 index 0000000000..fec625ca13 --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceStateProvider.kt @@ -0,0 +1,135 @@ +/* + * Copyright 2025 New Vector 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.space.impl.leave + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.matrix.api.room.join.JoinRule +import io.element.android.libraries.matrix.api.spaces.SpaceRoom +import io.element.android.libraries.previewutils.room.aSpaceRoom +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList + +class LeaveSpaceStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aLeaveSpaceState(), + aLeaveSpaceState( + spaceName = null, + selectableSpaceRooms = AsyncData.Success(persistentListOf()), + ), + aLeaveSpaceState( + selectableSpaceRooms = AsyncData.Success( + persistentListOf( + aSelectableSpaceRoom( + spaceRoom = aSpaceRoom( + displayName = "A long space name that should be truncated", + worldReadable = true, + ), + isLastAdmin = true, + ), + aSelectableSpaceRoom( + spaceRoom = aSpaceRoom( + joinRule = JoinRule.Private, + ), + isSelected = false, + ), + ) + ) + ), + aLeaveSpaceState( + selectableSpaceRooms = AsyncData.Success( + persistentListOf( + aSelectableSpaceRoom( + spaceRoom = aSpaceRoom( + worldReadable = true, + ), + isLastAdmin = true, + ), + aSelectableSpaceRoom( + spaceRoom = aSpaceRoom( + joinRule = JoinRule.Private, + ), + isSelected = true, + ), + ) + ) + ), + aLeaveSpaceState( + selectableSpaceRooms = AsyncData.Success( + persistentListOf( + aSelectableSpaceRoom( + spaceRoom = aSpaceRoom( + worldReadable = true, + ), + isLastAdmin = true, + ), + ) + ), + ), + aLeaveSpaceState( + selectableSpaceRooms = AsyncData.Success( + persistentListOf( + aSelectableSpaceRoom( + spaceRoom = aSpaceRoom( + worldReadable = true, + ), + isLastAdmin = true, + ), + aSelectableSpaceRoom( + spaceRoom = aSpaceRoom(), + isLastAdmin = true, + ), + ) + ), + ), + aLeaveSpaceState( + selectableSpaceRooms = AsyncData.Success( + List(10) { aSelectableSpaceRoom() }.toImmutableList() + ), + leaveSpaceAction = AsyncAction.Loading, + ), + aLeaveSpaceState( + selectableSpaceRooms = AsyncData.Success( + List(10) { aSelectableSpaceRoom() }.toImmutableList() + ), + leaveSpaceAction = AsyncAction.Failure(Exception("An error")), + ), + aLeaveSpaceState( + selectableSpaceRooms = AsyncData.Failure(Exception("An error")), + ), + aLeaveSpaceState( + isLastAdmin = true, + ), + ) +} + +fun aLeaveSpaceState( + spaceName: String? = "Space name", + isLastAdmin: Boolean = false, + selectableSpaceRooms: AsyncData> = AsyncData.Uninitialized, + leaveSpaceAction: AsyncAction = AsyncAction.Uninitialized, +) = LeaveSpaceState( + spaceName = spaceName, + isLastAdmin = isLastAdmin, + selectableSpaceRooms = selectableSpaceRooms, + leaveSpaceAction = leaveSpaceAction, + eventSink = { } +) + +fun aSelectableSpaceRoom( + spaceRoom: SpaceRoom = aSpaceRoom(), + isLastAdmin: Boolean = false, + isSelected: Boolean = false, +) = SelectableSpaceRoom( + spaceRoom = spaceRoom, + isLastAdmin = isLastAdmin, + isSelected = isSelected, +) 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 new file mode 100644 index 0000000000..7432301f91 --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt @@ -0,0 +1,349 @@ +/* + * Copyright 2025 New Vector 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.features.space.impl.leave + +import androidx.annotation.StringRes +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.selection.toggleable +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.space.impl.R +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule +import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule +import io.element.android.libraries.designsystem.components.BigIcon +import io.element.android.libraries.designsystem.components.async.AsyncActionView +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.avatar.Avatar +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.components.avatar.AvatarType +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.Button +import io.element.android.libraries.designsystem.theme.components.Checkbox +import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.IconSource +import io.element.android.libraries.designsystem.theme.components.Scaffold +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.TextButton +import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.matrix.api.room.join.JoinRule +import io.element.android.libraries.matrix.ui.model.getAvatarData +import io.element.android.libraries.ui.strings.CommonPlurals +import io.element.android.libraries.ui.strings.CommonStrings + +/** + * https://www.figma.com/design/kcnHxunG1LDWXsJhaNuiHz/ER-145--Spaces-on-Element-X?node-id=3947-68767&t=GTf1cLkAf6UCQDan-0 + */ +@Composable +fun LeaveSpaceView( + state: LeaveSpaceState, + onCancel: () -> Unit, + modifier: Modifier = Modifier, +) { + Scaffold( + modifier = modifier, + topBar = { + LeaveSpaceHeader( + state = state, + onBackClick = onCancel, + ) + }, + containerColor = ElementTheme.colors.bgCanvasDefault, + ) { padding -> + Column( + modifier = Modifier + .padding(padding) + .imePadding() + .consumeWindowInsets(padding) + .fillMaxSize() + ) { + LazyColumn( + modifier = Modifier + .weight(1f), + ) { + if (state.isLastAdmin.not()) { + when (state.selectableSpaceRooms) { + is AsyncData.Success -> { + // List rooms where the user is the only admin + state.selectableSpaceRooms.data.forEach { selectableSpaceRoom -> + item { + SpaceItem( + selectableSpaceRoom = selectableSpaceRoom, + showCheckBox = state.hasOnlyLastAdminRoom.not(), + onClick = { + state.eventSink(LeaveSpaceEvents.ToggleRoomSelection(selectableSpaceRoom.spaceRoom.roomId)) + } + ) + } + } + } + is AsyncData.Failure -> item { + AsyncFailure( + throwable = state.selectableSpaceRooms.error, + onRetry = { + state.eventSink(LeaveSpaceEvents.Retry) + }, + ) + } + is AsyncData.Loading, + AsyncData.Uninitialized -> item { + AsyncLoading() + } + } + } + } + LeaveSpaceButtons( + showLeaveButton = state.showLeaveButton, + selectedRoomsCount = state.selectedRoomsCount, + onLeaveSpace = { + state.eventSink(LeaveSpaceEvents.LeaveSpace) + }, + onCancel = onCancel, + ) + } + } + + AsyncActionView( + async = state.leaveSpaceAction, + onSuccess = { /* Nothing to do, the screen will be dismissed automatically */ }, + errorMessage = { stringResource(CommonStrings.error_unknown) }, + onErrorDismiss = { state.eventSink(LeaveSpaceEvents.CloseError) }, + ) +} + +@Composable +private fun LeaveSpaceHeader( + state: LeaveSpaceState, + onBackClick: () -> Unit, +) { + Column { + TopAppBar( + navigationIcon = { + BackButton(onClick = onBackClick) + }, + title = {}, + ) + IconTitleSubtitleMolecule( + modifier = Modifier.padding(top = 0.dp, bottom = 8.dp, start = 24.dp, end = 24.dp), + iconStyle = BigIcon.Style.AlertSolid, + title = stringResource( + if (state.isLastAdmin) R.string.screen_leave_space_title_last_admin else R.string.screen_leave_space_title, + state.spaceName ?: stringResource(CommonStrings.common_space) + ), + subTitle = + if (state.isLastAdmin) { + stringResource(R.string.screen_leave_space_subtitle_last_admin) + } else if (state.selectableSpaceRooms is AsyncData.Success && state.selectableSpaceRooms.data.isNotEmpty()) { + if (state.hasOnlyLastAdminRoom) { + stringResource(R.string.screen_leave_space_subtitle_only_last_admin) + } else { + stringResource(R.string.screen_leave_space_subtitle) + } + } else { + null + }, + ) + if (state.showQuickAction) { + if (state.areAllSelected) { + QuickActionButton(CommonStrings.action_deselect_all) { + state.eventSink(LeaveSpaceEvents.DeselectAllRooms) + } + } else { + QuickActionButton(resId = CommonStrings.action_select_all) { + state.eventSink(LeaveSpaceEvents.SelectAllRooms) + } + } + } + } +} + +@Composable +private fun ColumnScope.QuickActionButton( + @StringRes resId: Int, + onClick: () -> Unit, +) { + Text( + modifier = Modifier + .align(Alignment.End) + .padding(end = 8.dp) + .clickable(onClick = onClick) + .padding(8.dp), + text = stringResource(resId), + color = ElementTheme.colors.textActionPrimary, + style = ElementTheme.typography.fontBodyMdMedium, + ) +} + +@Composable +private fun LeaveSpaceButtons( + showLeaveButton: Boolean, + selectedRoomsCount: Int, + onLeaveSpace: () -> Unit, + onCancel: () -> Unit, +) { + ButtonColumnMolecule( + modifier = Modifier.padding(16.dp) + ) { + if (showLeaveButton) { + val text = if (selectedRoomsCount > 0) { + pluralStringResource(R.plurals.screen_leave_space_submit, selectedRoomsCount, selectedRoomsCount) + } else { + stringResource(CommonStrings.action_leave_space) + } + Button( + modifier = Modifier.fillMaxWidth(), + text = text, + leadingIcon = IconSource.Vector(CompoundIcons.Leave()), + onClick = onLeaveSpace, + destructive = true, + ) + } + // TODO For least admin space, add a button to open the settings. + // See https://www.figma.com/design/kcnHxunG1LDWXsJhaNuiHz/ER-145--Spaces-on-Element-X?node-id=4622-59600 + TextButton( + modifier = Modifier.fillMaxWidth(), + text = stringResource(CommonStrings.action_cancel), + onClick = onCancel, + ) + } +} + +@Composable +private fun SpaceItem( + selectableSpaceRoom: SelectableSpaceRoom, + showCheckBox: Boolean, + onClick: () -> Unit, +) { + val room = selectableSpaceRoom.spaceRoom + Row( + modifier = Modifier + .fillMaxWidth() + .heightIn(min = 66.dp) + .toggleable( + value = selectableSpaceRoom.isSelected, + role = Role.Checkbox, + enabled = selectableSpaceRoom.isLastAdmin.not(), + onValueChange = { onClick() } + ) + .clickable( + enabled = selectableSpaceRoom.isLastAdmin.not(), + // TODO + onClickLabel = null, + role = Role.Checkbox, + onClick = onClick, + ), + verticalAlignment = Alignment.CenterVertically, + ) { + Avatar( + modifier = Modifier.padding(horizontal = 16.dp), + avatarData = room.getAvatarData(AvatarSize.LeaveSpaceRoom), + avatarType = if (room.isSpace) AvatarType.Space() else AvatarType.Room(), + ) + Column( + modifier = Modifier.weight(1f), + ) { + Text( + modifier = Modifier + .padding(end = 16.dp), + text = room.displayName, + color = ElementTheme.colors.textPrimary, + style = ElementTheme.typography.fontBodyLgMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + if (room.joinRule == JoinRule.Private) { + // Picto for private + Icon( + modifier = Modifier + .size(16.dp) + .padding(end = 4.dp), + imageVector = CompoundIcons.LockSolid(), + contentDescription = null, + tint = ElementTheme.colors.iconTertiary, + ) + } else if (room.worldReadable) { + // Picto for world readable + Icon( + modifier = Modifier + .size(16.dp) + .padding(end = 4.dp), + imageVector = CompoundIcons.Public(), + contentDescription = null, + tint = ElementTheme.colors.iconTertiary, + ) + } + // Number of members + val membersCount = pluralStringResource( + CommonPlurals.common_member_count, + room.numJoinedMembers, + room.numJoinedMembers + ) + val subTitle = if (selectableSpaceRoom.isLastAdmin) { + stringResource(R.string.screen_leave_space_last_admin_info, membersCount) + } else { + membersCount + } + Text( + modifier = Modifier.padding(end = 16.dp), + text = subTitle, + color = ElementTheme.colors.textSecondary, + style = ElementTheme.typography.fontBodyMdRegular, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } + } + if (showCheckBox) { + Checkbox( + checked = selectableSpaceRoom.isSelected, + onCheckedChange = null, + enabled = selectableSpaceRoom.isLastAdmin.not(), + ) + } + } +} + +@PreviewsDayNight +@Composable +internal fun LeaveSpaceViewPreview( + @PreviewParameter(LeaveSpaceStateProvider::class) state: LeaveSpaceState, +) = ElementPreview { + LeaveSpaceView( + state = state, + onCancel = {}, + ) +} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/SelectableSpaceRoom.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/SelectableSpaceRoom.kt new file mode 100644 index 0000000000..6247a9e48f --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/SelectableSpaceRoom.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2025 New Vector 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.space.impl.leave + +import io.element.android.libraries.matrix.api.spaces.SpaceRoom + +data class SelectableSpaceRoom( + val spaceRoom: SpaceRoom, + val isLastAdmin: Boolean, + val isSelected: Boolean, +) 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/SpaceEvents.kt new file mode 100644 index 0000000000..8c8eb8dbb7 --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceEvents.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2025 New Vector 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.space.impl.root + +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 + + data class ShowTopicViewer(val topic: String) : SpaceEvents + data object HideTopicViewer : SpaceEvents +} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceNode.kt new file mode 100644 index 0000000000..52c3472182 --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceNode.kt @@ -0,0 +1,97 @@ +/* + * Copyright 2025 New Vector 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.space.impl.root + +import android.content.Context +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.lifecycle.lifecycleScope +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.Assisted +import dev.zacsweers.metro.AssistedInject +import io.element.android.annotations.ContributesNode +import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteView +import io.element.android.features.space.impl.di.SpaceFlowScope +import io.element.android.libraries.androidutils.R +import io.element.android.libraries.androidutils.system.startSharePlainTextIntent +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.spaces.SpaceRoomList +import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.coroutines.launch +import timber.log.Timber + +@ContributesNode(SpaceFlowScope::class) +@AssistedInject +class SpaceNode( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + private val presenter: SpacePresenter, + private val matrixClient: MatrixClient, + private val spaceRoomList: SpaceRoomList, + private val acceptDeclineInviteView: AcceptDeclineInviteView, +) : Node(buildContext, plugins = plugins) { + interface Callback : Plugin { + fun onOpenRoom(roomId: RoomId, viaParameters: List) + fun onLeaveSpace() + } + + private val callback = plugins.filterIsInstance().single() + + private fun onShareRoom(context: Context) = lifecycleScope.launch { + matrixClient.getRoom(spaceRoomList.roomId)?.use { room -> + room.getPermalink() + .onSuccess { permalink -> + context.startSharePlainTextIntent( + activityResultLauncher = null, + chooserTitle = context.getString(CommonStrings.common_share_space), + text = permalink, + noActivityFoundMessage = context.getString(R.string.error_no_compatible_app_found) + ) + } + .onFailure { + Timber.e(it) + } + } + } + + @Composable + override fun View(modifier: Modifier) { + val state = presenter.present() + val context = LocalContext.current + SpaceView( + state = state, + onBackClick = ::navigateUp, + onLeaveSpaceClick = { + callback.onLeaveSpace() + }, + onRoomClick = { spaceRoom -> + callback.onOpenRoom(spaceRoom.roomId, spaceRoom.via) + }, + onShareSpace = { + onShareRoom(context) + }, + acceptDeclineInviteView = { + acceptDeclineInviteView.Render( + state = state.acceptDeclineInviteState, + onAcceptInviteSuccess = { roomId -> + callback.onOpenRoom(roomId, emptyList()) + }, + onDeclineInviteSuccess = { roomId -> + // No action needed + }, + modifier = Modifier + ) + }, + modifier = modifier + ) + } +} 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 new file mode 100644 index 0000000000..58f5c8f5fb --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt @@ -0,0 +1,153 @@ +/* + * Copyright 2025 New Vector 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.space.impl.root + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import dev.zacsweers.metro.Inject +import im.vector.app.features.analytics.plan.JoinedRoom +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.AcceptDeclineInviteState +import io.element.android.features.invite.api.toInviteData +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.core.coroutine.mapState +import io.element.android.libraries.di.annotations.SessionCoroutineScope +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias +import io.element.android.libraries.matrix.api.room.CurrentUserMembership +import io.element.android.libraries.matrix.api.room.join.JoinRoom +import io.element.android.libraries.matrix.api.spaces.SpaceRoom +import io.element.android.libraries.matrix.api.spaces.SpaceRoomList +import io.element.android.libraries.matrix.ui.safety.rememberHideInvitesAvatar +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentSetOf +import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toImmutableMap +import kotlinx.collections.immutable.toImmutableSet +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import kotlin.jvm.optionals.getOrNull + +@Inject +class SpacePresenter( + private val spaceRoomList: SpaceRoomList, + private val client: MatrixClient, + private val seenInvitesStore: SeenInvitesStore, + private val joinRoom: JoinRoom, + private val acceptDeclineInvitePresenter: Presenter, + @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, +) : Presenter { + private var children by mutableStateOf>(persistentListOf()) + + @Composable + override fun present(): SpaceState { + LaunchedEffect(Unit) { + paginate() + spaceRoomList.spaceRoomsFlow.collect { children = it.toImmutableList() } + } + + val hideInvitesAvatar by client.rememberHideInvitesAvatar() + val seenSpaceInvites by remember { + seenInvitesStore.seenRoomIds().map { it.toImmutableSet() } + }.collectAsState(persistentSetOf()) + + val localCoroutineScope = rememberCoroutineScope() + + val hasMoreToLoad by remember { + spaceRoomList.paginationStatusFlow.mapState { status -> + when (status) { + is SpaceRoomList.PaginationStatus.Idle -> status.hasMoreToLoad + SpaceRoomList.PaginationStatus.Loading -> true + } + } + }.collectAsState() + + val currentSpace by spaceRoomList.currentSpaceFlow.collectAsState() + val (joinActions, setJoinActions) = remember { mutableStateOf(emptyMap>()) } + + var topicViewerState: TopicViewerState by remember { mutableStateOf(TopicViewerState.Hidden) } + + LaunchedEffect(children) { + // Remove joined children from the join actions + val joinedChildren = children + .filter { it.state == CurrentUserMembership.JOINED } + .map { it.roomId } + setJoinActions(joinActions - joinedChildren) + } + + val acceptDeclineInviteState = acceptDeclineInvitePresenter.present() + + fun handleEvents(event: SpaceEvents) { + when (event) { + SpaceEvents.LoadMore -> localCoroutineScope.paginate() + is SpaceEvents.Join -> { + sessionCoroutineScope.joinRoom(event.spaceRoom, joinActions, setJoinActions) + } + SpaceEvents.ClearFailures -> { + val failedActions = joinActions + .filterValues { it is AsyncAction.Failure } + .mapValues { AsyncAction.Uninitialized } + setJoinActions(joinActions + failedActions) + } + is SpaceEvents.AcceptInvite -> { + acceptDeclineInviteState.eventSink( + AcceptDeclineInviteEvents.AcceptInvite(event.spaceRoom.toInviteData()) + ) + } + is SpaceEvents.DeclineInvite -> { + acceptDeclineInviteState.eventSink( + AcceptDeclineInviteEvents.DeclineInvite(invite = event.spaceRoom.toInviteData(), shouldConfirm = true, blockUser = false) + ) + } + SpaceEvents.HideTopicViewer -> topicViewerState = TopicViewerState.Hidden + is SpaceEvents.ShowTopicViewer -> topicViewerState = TopicViewerState.Shown(event.topic) + } + } + return SpaceState( + currentSpace = currentSpace.getOrNull(), + children = children, + seenSpaceInvites = seenSpaceInvites, + hideInvitesAvatar = hideInvitesAvatar, + hasMoreToLoad = hasMoreToLoad, + joinActions = joinActions.toImmutableMap(), + acceptDeclineInviteState = acceptDeclineInviteState, + topicViewerState = topicViewerState, + eventSink = ::handleEvents, + ) + } + + private fun CoroutineScope.joinRoom( + spaceRoom: SpaceRoom, + joinActions: Map>, + setJoinActions: (Map>) -> Unit + ) = launch { + setJoinActions(joinActions + mapOf(spaceRoom.roomId to AsyncAction.Loading)) + joinRoom.invoke( + roomIdOrAlias = spaceRoom.roomId.toRoomIdOrAlias(), + serverNames = spaceRoom.via, + trigger = JoinedRoom.Trigger.SpaceHierarchy, + ).onFailure { + setJoinActions(joinActions + mapOf(spaceRoom.roomId to AsyncAction.Failure(it))) + } + } + + private fun CoroutineScope.paginate() = launch { + spaceRoomList.paginate() + } +} 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 new file mode 100644 index 0000000000..2499e4c046 --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceState.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2025 New Vector 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.space.impl.root + +import androidx.compose.runtime.Immutable +import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.spaces.SpaceRoom +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableMap +import kotlinx.collections.immutable.ImmutableSet + +data class SpaceState( + val currentSpace: SpaceRoom?, + val children: ImmutableList, + val seenSpaceInvites: ImmutableSet, + val hideInvitesAvatar: Boolean, + val hasMoreToLoad: Boolean, + val joinActions: ImmutableMap>, + val acceptDeclineInviteState: AcceptDeclineInviteState, + val topicViewerState: TopicViewerState, + val eventSink: (SpaceEvents) -> Unit +) { + fun isJoining(spaceId: RoomId): Boolean = joinActions[spaceId] == AsyncAction.Loading + val hasAnyFailure: Boolean = joinActions.values.any { + it is AsyncAction.Failure + } +} + +@Immutable +sealed interface TopicViewerState { + data object Hidden : TopicViewerState + data class Shown(val topic: String) : TopicViewerState +} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt new file mode 100644 index 0000000000..98a7363abb --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt @@ -0,0 +1,96 @@ +/* + * Copyright 2025 New Vector 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.space.impl.root + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.tooling.preview.datasource.LoremIpsum +import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState +import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInviteState +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.CurrentUserMembership +import io.element.android.libraries.matrix.api.room.join.JoinRule +import io.element.android.libraries.matrix.api.spaces.SpaceRoom +import io.element.android.libraries.previewutils.room.aSpaceRoom +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toImmutableMap +import kotlinx.collections.immutable.toImmutableSet + +open class SpaceStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aSpaceState(), + aSpaceState(parentSpace = aParentSpace(joinRule = JoinRule.Public)), + aSpaceState(parentSpace = aParentSpace(joinRule = JoinRule.Restricted(persistentListOf()))), + aSpaceState(children = aListOfSpaceRooms()), + aSpaceState( + parentSpace = aParentSpace(), + children = aListOfSpaceRooms(), + joiningRooms = setOf(RoomId("!spaceId0:example.com")), + hasMoreToLoad = false + ), + aSpaceState( + topicViewerState = TopicViewerState.Shown(topic = "Space description goes here." + LoremIpsum(20).values.first()), + ), + // Add other states here + ) +} + +fun aSpaceState( + parentSpace: SpaceRoom? = aParentSpace(), + children: List = emptyList(), + seenSpaceInvites: Set = emptySet(), + joiningRooms: Set = emptySet(), + joinActions: Map> = joiningRooms.associateWith { AsyncAction.Loading }, + hideInvitesAvatar: Boolean = false, + hasMoreToLoad: Boolean = true, + acceptDeclineInviteState: AcceptDeclineInviteState = anAcceptDeclineInviteState(), + topicViewerState: TopicViewerState = TopicViewerState.Hidden, + eventSink: (SpaceEvents) -> Unit = { }, +) = SpaceState( + currentSpace = parentSpace, + children = children.toImmutableList(), + seenSpaceInvites = seenSpaceInvites.toImmutableSet(), + hideInvitesAvatar = hideInvitesAvatar, + hasMoreToLoad = hasMoreToLoad, + joinActions = joinActions.toImmutableMap(), + acceptDeclineInviteState = acceptDeclineInviteState, + topicViewerState = topicViewerState, + eventSink = eventSink, +) + +private fun aParentSpace( + joinRule: JoinRule? = null, +): SpaceRoom { + return aSpaceRoom( + numJoinedMembers = 5, + childrenCount = 10, + worldReadable = true, + joinRule = joinRule, + roomId = RoomId("!spaceId0:example.com"), + topic = "Space description goes here. " + LoremIpsum(20).values.first(), + ) +} + +private fun aListOfSpaceRooms(): List { + return listOf( + aSpaceRoom( + roomId = RoomId("!spaceId0:example.com"), + state = null, + ), + aSpaceRoom( + roomId = RoomId("!spaceId1:example.com"), + state = CurrentUserMembership.JOINED, + ), + aSpaceRoom( + roomId = RoomId("!spaceId2:example.com"), + state = CurrentUserMembership.INVITED, + ), + ) +} 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 new file mode 100644 index 0000000000..46c6a10e57 --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt @@ -0,0 +1,387 @@ +/* + * Copyright 2025 New Vector 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.space.impl.root + +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.ExperimentalMaterial3Api +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.rememberUpdatedState +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.heading +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.libraries.designsystem.atomic.molecules.InviteButtonsRowMolecule +import io.element.android.libraries.designsystem.components.ClickableLinkText +import io.element.android.libraries.designsystem.components.SimpleModalBottomSheet +import io.element.android.libraries.designsystem.components.async.AsyncIndicator +import io.element.android.libraries.designsystem.components.async.AsyncIndicatorHost +import io.element.android.libraries.designsystem.components.async.rememberAsyncIndicatorState +import io.element.android.libraries.designsystem.components.avatar.Avatar +import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.components.avatar.AvatarType +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.CircularProgressIndicator +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 +import io.element.android.libraries.designsystem.theme.components.IconButton +import io.element.android.libraries.designsystem.theme.components.Scaffold +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.matrix.api.room.CurrentUserMembership +import io.element.android.libraries.matrix.api.spaces.SpaceRoom +import io.element.android.libraries.matrix.ui.components.JoinButton +import io.element.android.libraries.matrix.ui.components.SpaceHeaderView +import io.element.android.libraries.matrix.ui.components.SpaceRoomItemView +import io.element.android.libraries.matrix.ui.model.getAvatarData +import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.delay + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SpaceView( + state: SpaceState, + onBackClick: () -> Unit, + onRoomClick: (spaceRoom: SpaceRoom) -> Unit, + onShareSpace: () -> Unit, + onLeaveSpaceClick: () -> Unit, + modifier: Modifier = Modifier, + acceptDeclineInviteView: @Composable () -> Unit, +) { + Scaffold( + modifier = modifier, + topBar = { + SpaceViewTopBar( + currentSpace = state.currentSpace, + onBackClick = onBackClick, + onLeaveSpaceClick = onLeaveSpaceClick, + onShareSpace = onShareSpace, + ) + }, + content = { padding -> + Box( + modifier = Modifier.padding(padding) + ) { + SpaceViewContent( + state = state, + onRoomClick = onRoomClick, + onTopicClick = { topic -> + state.eventSink(SpaceEvents.ShowTopicViewer(topic)) + } + ) + JoinRoomFailureEffect( + hasAnyFailure = state.hasAnyFailure, + eventSink = state.eventSink + ) + acceptDeclineInviteView() + } + }, + ) + if (state.topicViewerState is TopicViewerState.Shown) { + TopicViewerBottomSheet( + topicViewerState = state.topicViewerState, + onDismiss = { + state.eventSink(SpaceEvents.HideTopicViewer) + } + ) + } +} + +@Composable +private fun JoinRoomFailureEffect( + hasAnyFailure: Boolean, + eventSink: (SpaceEvents) -> Unit, +) { + val asyncIndicatorState = rememberAsyncIndicatorState() + val updatedEventSink by rememberUpdatedState(eventSink) + AsyncIndicatorHost(modifier = Modifier, asyncIndicatorState) + LaunchedEffect(hasAnyFailure) { + if (hasAnyFailure) { + asyncIndicatorState.enqueue { + AsyncIndicator.Failure(text = stringResource(CommonStrings.common_something_went_wrong)) + } + delay(AsyncIndicator.DURATION_SHORT) + updatedEventSink(SpaceEvents.ClearFailures) + } else { + asyncIndicatorState.clear() + } + } +} + +@Composable +private fun TopicViewerBottomSheet( + topicViewerState: TopicViewerState.Shown, + onDismiss: () -> Unit, + modifier: Modifier = Modifier, +) { + SimpleModalBottomSheet( + title = stringResource(CommonStrings.common_description), + onDismiss = onDismiss, + modifier = modifier + ) { + ClickableLinkText( + text = topicViewerState.topic, + interactionSource = remember { MutableInteractionSource() }, + style = ElementTheme.typography.fontBodyMdRegular, + color = ElementTheme.colors.textSecondary, + ) + } +} + +@Composable +private fun SpaceViewContent( + state: SpaceState, + onRoomClick: (spaceRoom: SpaceRoom) -> Unit, + onTopicClick: (String) -> Unit, + modifier: Modifier = Modifier, +) { + LazyColumn(modifier.fillMaxSize()) { + val currentSpace = state.currentSpace + if (currentSpace != null) { + item { + SpaceHeaderView( + avatarData = currentSpace.getAvatarData(AvatarSize.SpaceHeader), + name = currentSpace.displayName, + topic = currentSpace.topic, + topicMaxLines = 2, + visibility = currentSpace.visibility, + heroes = currentSpace.heroes.toImmutableList(), + numberOfMembers = currentSpace.numJoinedMembers, + onTopicClick = onTopicClick + ) + } + } + state.children.forEach { spaceRoom -> + item { + val isInvitation = spaceRoom.state == CurrentUserMembership.INVITED + val isCurrentlyJoining = state.isJoining(spaceRoom.roomId) + SpaceRoomItemView( + spaceRoom = spaceRoom, + showUnreadIndicator = isInvitation && spaceRoom.roomId !in state.seenSpaceInvites, + hideAvatars = isInvitation && state.hideInvitesAvatar, + onClick = { + onRoomClick(spaceRoom) + }, + onLongClick = { + // TODO + }, + trailingAction = spaceRoom.trailingAction(isCurrentlyJoining = isCurrentlyJoining) { + state.eventSink(SpaceEvents.Join(spaceRoom)) + }, + bottomAction = spaceRoom.inviteButtons( + onAcceptClick = { + state.eventSink(SpaceEvents.AcceptInvite(spaceRoom)) + }, + onDeclineClick = { + state.eventSink(SpaceEvents.DeclineInvite(spaceRoom)) + } + ) + ) + } + } + if (state.hasMoreToLoad) { + item { + LoadingMoreIndicator(eventSink = state.eventSink) + } + } + } +} + +@Composable +private fun LoadingMoreIndicator( + eventSink: (SpaceEvents) -> Unit, + modifier: Modifier = Modifier +) { + Box( + modifier = modifier.fillMaxWidth(), + contentAlignment = Alignment.Center, + ) { + CircularProgressIndicator( + strokeWidth = 2.dp, + modifier = Modifier.padding(vertical = 8.dp) + ) + val latestEventSink by rememberUpdatedState(eventSink) + LaunchedEffect(Unit) { + latestEventSink(SpaceEvents.LoadMore) + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun SpaceViewTopBar( + currentSpace: SpaceRoom?, + onBackClick: () -> Unit, + onLeaveSpaceClick: () -> Unit, + onShareSpace: () -> Unit, + modifier: Modifier = Modifier, +) { + TopAppBar( + modifier = modifier, + navigationIcon = { + BackButton(onClick = onBackClick) + }, + title = { + if (currentSpace != null) { + SpaceAvatarAndNameRow( + name = currentSpace.displayName, + avatarData = currentSpace.getAvatarData(AvatarSize.TimelineRoom), + ) + } + }, + actions = { + var showMenu by remember { mutableStateOf(false) } + IconButton( + onClick = { showMenu = !showMenu } + ) { + Icon( + imageVector = CompoundIcons.OverflowVertical(), + contentDescription = null, + ) + } + DropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false } + ) { + DropdownMenuItem( + onClick = { + showMenu = false + onShareSpace() + }, + text = { Text(stringResource(id = CommonStrings.action_share)) }, + leadingIcon = { + Icon( + imageVector = CompoundIcons.ShareAndroid(), + tint = ElementTheme.colors.iconSecondary, + contentDescription = null, + ) + } + ) + DropdownMenuItem( + onClick = { + showMenu = false + onLeaveSpaceClick() + }, + text = { + Text( + text = stringResource(id = CommonStrings.action_leave), + color = ElementTheme.colors.textCriticalPrimary, + ) + }, + leadingIcon = { + Icon( + imageVector = CompoundIcons.Leave(), + tint = ElementTheme.colors.iconCriticalPrimary, + contentDescription = null, + ) + } + ) + } + }, + ) +} + +@Composable +private fun SpaceAvatarAndNameRow( + name: String?, + avatarData: AvatarData, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically + ) { + Avatar( + avatarData = avatarData, + avatarType = AvatarType.Space(), + ) + Text( + modifier = Modifier + .padding(horizontal = 8.dp) + .semantics { + heading() + }, + text = name ?: stringResource(CommonStrings.common_no_space_name), + style = ElementTheme.typography.fontBodyLgMedium, + fontStyle = FontStyle.Italic.takeIf { name == null }, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } +} + +private fun SpaceRoom.trailingAction( + isCurrentlyJoining: Boolean, + onClick: () -> Unit +): @Composable (() -> Unit)? { + return when (state) { + null, CurrentUserMembership.LEFT -> { + { + JoinButton( + showProgress = isCurrentlyJoining, + onClick = onClick, + ) + } + } + else -> null + } +} + +private fun SpaceRoom.inviteButtons( + onAcceptClick: () -> Unit, + onDeclineClick: () -> Unit, +): @Composable (() -> Unit)? { + return when (state) { + CurrentUserMembership.INVITED -> { + @Composable { + InviteButtonsRowMolecule( + onAcceptClick = onAcceptClick, + onDeclineClick = onDeclineClick, + ) + } + } + else -> null + } +} + +@PreviewsDayNight +@Composable +internal fun SpaceViewPreview( + @PreviewParameter(SpaceStateProvider::class) state: SpaceState +) = ElementPreview { + SpaceView( + state = state, + onRoomClick = {}, + onShareSpace = {}, + onLeaveSpaceClick = {}, + acceptDeclineInviteView = {}, + onBackClick = {}, + ) +} diff --git a/features/space/impl/src/main/res/values-cs/translations.xml b/features/space/impl/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..0c645f0650 --- /dev/null +++ b/features/space/impl/src/main/res/values-cs/translations.xml @@ -0,0 +1,11 @@ + + + "%1$s (Správce)" + + "Opustit %1$d místnost a prostor" + "Opustit %1$d místnosti a prostor" + "Opustit %1$d místností a prostor" + + "Tím budete také odstraněni ze všech místností v tomto prostoru." + "Opustit %1$s?" + diff --git a/features/space/impl/src/main/res/values-cy/translations.xml b/features/space/impl/src/main/res/values-cy/translations.xml new file mode 100644 index 0000000000..d9d6c02ebe --- /dev/null +++ b/features/space/impl/src/main/res/values-cy/translations.xml @@ -0,0 +1,14 @@ + + + "%1$s (Gweinyddwr)" + + "Gadael %1$d ystafelloedd a gofodau" + "Gadael %1$d ystafell a gofod" + "Gadael %1$d ystafell a gofod" + "Gadael %1$d ystafell a gofod" + "Gadael %1$d ystafell a gofod" + "Gadael %1$d ystafell a gofod" + + "Dewiswch yr ystafelloedd yr hoffech chi eu gadael nad chi yw\'r unig weinyddwr ar eu cyfer:" + "Gadael %1$s ?" + diff --git a/features/space/impl/src/main/res/values-da/translations.xml b/features/space/impl/src/main/res/values-da/translations.xml new file mode 100644 index 0000000000..7953f32e99 --- /dev/null +++ b/features/space/impl/src/main/res/values-da/translations.xml @@ -0,0 +1,13 @@ + + + "%1$s (Admin)" + + "Forlad %1$d rum og klynge" + "Forlad %1$d rum og klynger" + + "Vælg de rum, du vil forlade, som du ikke er den eneste administrator for:" + "Du skal tildele en anden administrator til denne klynge, før du kan forlade den." + "Du vil ikke blive fjernet fra følgende rum, fordi du er den eneste administrator:" + "Forlad %1$s?" + "Du er den eneste administrator for %1$s" + diff --git a/features/space/impl/src/main/res/values-de/translations.xml b/features/space/impl/src/main/res/values-de/translations.xml new file mode 100644 index 0000000000..6fd5d7c76b --- /dev/null +++ b/features/space/impl/src/main/res/values-de/translations.xml @@ -0,0 +1,13 @@ + + + "%1$s (Admin)" + + "%1$d Chat und Space verlassen" + "%1$d Chats und Space verlassen" + + "Dadurch wirst du auch aus allen Chats in diesem Space entfernt." + "Du musst einen anderen Admin für diesen Space zuweisen, bevor du ihn verlassen kannst." + "Du wirst aus den folgenden Chats nicht entfernt, weil du der einzige Admin bist:" + "%1$s verlassen?" + "Du bist der einzige Administrator für %1$s" + diff --git a/features/space/impl/src/main/res/values-et/translations.xml b/features/space/impl/src/main/res/values-et/translations.xml new file mode 100644 index 0000000000..3ab5d65284 --- /dev/null +++ b/features/space/impl/src/main/res/values-et/translations.xml @@ -0,0 +1,11 @@ + + + "%1$s (Peakasutaja)" + + "Lahku %1$d-st jututoast ja kogukonnast" + "Lahku %1$d-st jututoast ja kogukonnast" + + "Sellega eemaldad end ka kõikidest antud kogukonna jututubadest." + "Sind ei saa järgnevatest jututubadest eemaldada, kuna oled seal/neis ainus peakasutaja:" + "Kas lahkud %1$s kogukonnast?" + diff --git a/features/space/impl/src/main/res/values-fi/translations.xml b/features/space/impl/src/main/res/values-fi/translations.xml new file mode 100644 index 0000000000..501ff2f97d --- /dev/null +++ b/features/space/impl/src/main/res/values-fi/translations.xml @@ -0,0 +1,12 @@ + + + + "Poistu %1$d huoneesta ja tilasta" + "Poistu %1$d huoneesta ja tilasta" + + "Tämä poistaa sinut myös kaikista tämän tilan huoneista." + "Sinun on valittava tälle tilalle toinen ylläpitäjä ennen kuin voit poistua." + "Sinua ei poisteta seuraavista huoneista, koska olet ainoa ylläpitäjä:" + "Haluatko poistua tilasta %1$s?" + "Olet ainoa ylläpitäjä tilassa %1$s" + diff --git a/features/space/impl/src/main/res/values-fr/translations.xml b/features/space/impl/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..cf37795848 --- /dev/null +++ b/features/space/impl/src/main/res/values-fr/translations.xml @@ -0,0 +1,13 @@ + + + "%1$s (Admin)" + + "Quitter %1$d salon et l’espace" + "Quitter %1$d salons et l’espace" + + "Sélectionnez les salons que vous souhaitez quitter et dont vous n’êtes pas le seul administrateur:" + "Vous devez désigner un autre administrateur pour cet espace avant de pouvoir partir." + "Vous ne quitterez pas le ou les salons suivants car vous y êtes le seul administrateur:" + "Quitter %1$s?" + "Vous êtes le seul administrateur de %1$s" + diff --git a/features/space/impl/src/main/res/values-hu/translations.xml b/features/space/impl/src/main/res/values-hu/translations.xml new file mode 100644 index 0000000000..79a8733df2 --- /dev/null +++ b/features/space/impl/src/main/res/values-hu/translations.xml @@ -0,0 +1,10 @@ + + + "%1$s (Adminisztrátor)" + + "%1$d szoba és tér elhagyása" + "%1$d szoba és tér elhagyása" + + "Ez a tér összes szobájából is eltávolítja." + "Kilép innen: %1$s?" + diff --git a/features/space/impl/src/main/res/values-nb/translations.xml b/features/space/impl/src/main/res/values-nb/translations.xml new file mode 100644 index 0000000000..aafe1d756f --- /dev/null +++ b/features/space/impl/src/main/res/values-nb/translations.xml @@ -0,0 +1,9 @@ + + + "%1$s (Admin)" + "Velg rommene du vil forlate, som du ikke er den eneste administratoren for:" + "Du må tildele en annen administrator for dette området før du kan forlate det." + "Du vil ikke bli fjernet fra følgende rom fordi du er den eneste administratoren:" + "Forlat %1$s?" + "Du er den eneste administratoren for %1$s" + diff --git a/features/space/impl/src/main/res/values-pt/translations.xml b/features/space/impl/src/main/res/values-pt/translations.xml new file mode 100644 index 0000000000..75e1f5431e --- /dev/null +++ b/features/space/impl/src/main/res/values-pt/translations.xml @@ -0,0 +1,9 @@ + + + + "Sair do espaço e de %1$d sala" + "Sair do espaço e de %1$d salas" + + "Também irás sair de todas as salas deste espaço." + "Sair de %1$s?" + diff --git a/features/space/impl/src/main/res/values-ro/translations.xml b/features/space/impl/src/main/res/values-ro/translations.xml new file mode 100644 index 0000000000..6ff2a5dfa8 --- /dev/null +++ b/features/space/impl/src/main/res/values-ro/translations.xml @@ -0,0 +1,14 @@ + + + "%1$s (Admin)" + + "Părăsiți %1$d cameră și spațiul" + "Părăsiți %1$d camere și spațiul" + "Părăsiți %1$d camere și spațiul" + + "Selectați camerele pe care doriți să le părăsiți și în care nu sunteți singurul administrator:" + "Trebuie să desemnați un alt administrator pentru acest spațiu înainte de a-l părăsi." + "Nu veți părăsi următoarele camere deoarece sunteți singurul administrator:" + "Părăsiți %1$s?" + "Sunteți singurul administrator pentru %1$s" + diff --git a/features/space/impl/src/main/res/values-ru/translations.xml b/features/space/impl/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..0df1b9224a --- /dev/null +++ b/features/space/impl/src/main/res/values-ru/translations.xml @@ -0,0 +1,14 @@ + + + "%1$s (Администратор)" + + "Покинуть %1$d комнату и пространство" + "Покинуть %1$d комнат и пространство" + "Покинуть %1$d комнат и пространство" + + "Выберите комнаты, которые вы хотите покинуть и в которых вы не являетесь единственным администратором:" + "Прежде чем покинуть это пространство, вам необходимо назначить другого администратора." + "Вы не будете удалены из следующих комнат, поскольку вы являетесь единственным администратором:" + "Выйти из %1$s?" + "Вы единственный администратор для %1$s" + diff --git a/features/space/impl/src/main/res/values-zh-rTW/translations.xml b/features/space/impl/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..419ea40233 --- /dev/null +++ b/features/space/impl/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,5 @@ + + + "這也會將您從此空間中的所有聊天室移除。" + "離開 %1$s?" + diff --git a/features/space/impl/src/main/res/values-zh/translations.xml b/features/space/impl/src/main/res/values-zh/translations.xml new file mode 100644 index 0000000000..4e95141bad --- /dev/null +++ b/features/space/impl/src/main/res/values-zh/translations.xml @@ -0,0 +1,12 @@ + + + "%1$s (管理员)" + + "离开 %1$d 个房间和空间" + + "选择您想要离开且您不是其唯一管理员的房间:" + "您需要为该空间指定另一位管理员才能离开。" + "您不会从以下房间中被移除,因为您是唯一的管理员:" + "离开%1$s?" + "您是 %1$s 的唯一管理员" + diff --git a/features/space/impl/src/main/res/values/localazy.xml b/features/space/impl/src/main/res/values/localazy.xml new file mode 100644 index 0000000000..c6ced29d41 --- /dev/null +++ b/features/space/impl/src/main/res/values/localazy.xml @@ -0,0 +1,13 @@ + + + "%1$s (Admin)" + + "Leave %1$d room and space" + "Leave %1$d rooms and space" + + "Select the rooms you’d like to leave which you\'re not the only administrator for:" + "You need to assign another admin for this space before you can leave." + "You will not be removed from the following room(s) because you\'re the only administrator:" + "Leave %1$s?" + "You are the only admin for %1$s" + diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt index 465fde3425..17823ba72b 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt @@ -9,12 +9,12 @@ package io.element.android.features.space.impl 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.features.invite.test.InMemorySeenInvitesStore import io.element.android.features.space.api.SpaceEntryPoint +import io.element.android.features.space.impl.di.FakeSpaceFlowGraph import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.test.A_ROOM_ID -import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.spaces.FakeSpaceRoomList import io.element.android.libraries.matrix.test.spaces.FakeSpaceService import io.element.android.tests.testutils.lambda.lambdaError @@ -26,38 +26,31 @@ class DefaultSpaceEntryPointTest { @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() + @get:Rule + val mainDispatcherRule = MainDispatcherRule() + @Test fun `test node builder`() { val entryPoint = DefaultSpaceEntryPoint() val nodeInputs = SpaceEntryPoint.Inputs(A_ROOM_ID) val parentNode = TestParentNode.create { buildContext, plugins -> - SpaceNode( + SpaceFlowNode( buildContext = buildContext, plugins = plugins, - presenterFactory = { inputs -> - assertThat(inputs).isEqualTo(nodeInputs) - SpacePresenter( - inputs = inputs, - client = FakeMatrixClient( - spaceService = FakeSpaceService( - spaceRoomListResult = { FakeSpaceRoomList() }, - ) - ), - seenInvitesStore = InMemorySeenInvitesStore(), - ) - }, + spaceService = FakeSpaceService( + spaceRoomListResult = { _: RoomId -> FakeSpaceRoomList(A_ROOM_ID) } + ), + graphFactory = FakeSpaceFlowGraph.Factory ) } val callback = object : SpaceEntryPoint.Callback { - override fun onOpenRoom(roomId: RoomId) { - lambdaError() - } + override fun onOpenRoom(roomId: RoomId, viaParameters: List) = lambdaError() } val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) .inputs(nodeInputs) .callback(callback) .build() - assertThat(result).isInstanceOf(SpaceNode::class.java) + assertThat(result).isInstanceOf(SpaceFlowNode::class.java) assertThat(result.plugins).contains(nodeInputs) assertThat(result.plugins).contains(callback) } diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/SpacePresenterTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/SpacePresenterTest.kt deleted file mode 100644 index 0bcd1303ae..0000000000 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/SpacePresenterTest.kt +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2025 New Vector 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(ExperimentalCoroutinesApi::class) - -package io.element.android.features.space.impl - -import com.google.common.truth.Truth.assertThat -import io.element.android.features.invite.api.SeenInvitesStore -import io.element.android.features.invite.test.InMemorySeenInvitesStore -import io.element.android.features.space.api.SpaceEntryPoint -import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.api.spaces.SpaceRoomList -import io.element.android.libraries.matrix.test.A_ROOM_ID -import io.element.android.libraries.matrix.test.FakeMatrixClient -import io.element.android.libraries.matrix.test.spaces.FakeSpaceRoomList -import io.element.android.libraries.matrix.test.spaces.FakeSpaceService -import io.element.android.libraries.previewutils.room.aSpaceRoom -import io.element.android.tests.testutils.lambda.lambdaRecorder -import io.element.android.tests.testutils.test -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.advanceUntilIdle -import kotlinx.coroutines.test.runTest -import org.junit.Test - -class SpacePresenterTest { - @Test - fun `present - initial state`() = runTest { - val paginateResult = lambdaRecorder> { - Result.success(Unit) - } - val presenter = createSpacePresenter( - client = FakeMatrixClient( - spaceService = FakeSpaceService( - spaceRoomListResult = { - FakeSpaceRoomList( - paginateResult = paginateResult, - ) - }, - ), - ), - ) - presenter.test { - val state = awaitItem() - assertThat(state.currentSpace).isNull() - assertThat(state.children).isEmpty() - assertThat(state.seenSpaceInvites).isEmpty() - assertThat(state.hideInvitesAvatar).isFalse() - assertThat(state.hasMoreToLoad).isTrue() - advanceUntilIdle() - paginateResult.assertions().isCalledOnce() - } - } - - @Test - fun `present - load more`() = runTest { - val paginateResult = lambdaRecorder> { - Result.success(Unit) - } - val presenter = createSpacePresenter( - client = FakeMatrixClient( - spaceService = FakeSpaceService( - spaceRoomListResult = { - FakeSpaceRoomList( - paginateResult = paginateResult, - ) - }, - ), - ), - ) - presenter.test { - val state = awaitItem() - advanceUntilIdle() - paginateResult.assertions().isCalledOnce() - state.eventSink(SpaceEvents.LoadMore) - advanceUntilIdle() - paginateResult.assertions().isCalledExactly(2) - } - } - - @Test - fun `present - has more to load value`() = runTest { - val fakeSpaceRoomList = FakeSpaceRoomList( - paginateResult = { Result.success(Unit) }, - ) - val presenter = createSpacePresenter( - client = FakeMatrixClient( - spaceService = FakeSpaceService( - spaceRoomListResult = { fakeSpaceRoomList }, - ), - ), - ) - presenter.test { - val state = awaitItem() - advanceUntilIdle() - assertThat(state.hasMoreToLoad).isTrue() - fakeSpaceRoomList.emitPaginationStatus( - SpaceRoomList.PaginationStatus.Idle(hasMoreToLoad = false) - ) - assertThat(awaitItem().hasMoreToLoad).isFalse() - fakeSpaceRoomList.emitPaginationStatus( - SpaceRoomList.PaginationStatus.Idle(hasMoreToLoad = true) - ) - assertThat(awaitItem().hasMoreToLoad).isTrue() - } - } - - @Test - fun `present - current space value`() = runTest { - val fakeSpaceRoomList = FakeSpaceRoomList( - paginateResult = { Result.success(Unit) }, - ) - val presenter = createSpacePresenter( - client = FakeMatrixClient( - spaceService = FakeSpaceService( - spaceRoomListResult = { fakeSpaceRoomList }, - ), - ), - ) - presenter.test { - val state = awaitItem() - advanceUntilIdle() - assertThat(state.currentSpace).isNull() - val aSpace = aSpaceRoom() - fakeSpaceRoomList.emitCurrentSpace(aSpace) - assertThat(awaitItem().currentSpace).isEqualTo(aSpace) - } - } - - @Test - fun `present - children value`() = runTest { - val fakeSpaceRoomList = FakeSpaceRoomList( - paginateResult = { Result.success(Unit) }, - ) - val presenter = createSpacePresenter( - client = FakeMatrixClient( - spaceService = FakeSpaceService( - spaceRoomListResult = { fakeSpaceRoomList }, - ), - ), - ) - presenter.test { - val state = awaitItem() - advanceUntilIdle() - assertThat(state.children).isEmpty() - val aSpace = aSpaceRoom() - fakeSpaceRoomList.emitSpaceRooms(listOf(aSpace)) - assertThat(awaitItem().children).containsExactly(aSpace) - } - } - - private fun createSpacePresenter( - inputs: SpaceEntryPoint.Inputs = SpaceEntryPoint.Inputs(A_ROOM_ID), - client: MatrixClient = FakeMatrixClient(), - seenInvitesStore: SeenInvitesStore = InMemorySeenInvitesStore(), - ): SpacePresenter { - return SpacePresenter( - inputs = inputs, - client = client, - seenInvitesStore = seenInvitesStore, - ) - } -} diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/di/FakeSpaceFlowGraph.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/di/FakeSpaceFlowGraph.kt new file mode 100644 index 0000000000..09263ff52d --- /dev/null +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/di/FakeSpaceFlowGraph.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2025 New Vector 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.space.impl.di + +import com.bumble.appyx.core.node.Node +import io.element.android.libraries.architecture.AssistedNodeFactory +import io.element.android.libraries.matrix.api.spaces.SpaceRoomList +import kotlin.reflect.KClass + +class FakeSpaceFlowGraph : SpaceFlowGraph { + object Factory : SpaceFlowGraph.Factory { + override fun create(spaceRoomList: SpaceRoomList): SpaceFlowGraph { + return FakeSpaceFlowGraph() + } + } + + override fun nodeFactories(): Map, AssistedNodeFactory<*>> { + return emptyMap() + } +} 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 new file mode 100644 index 0000000000..3d123f3f41 --- /dev/null +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenterTest.kt @@ -0,0 +1,252 @@ +/* + * Copyright 2025 New Vector 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.space.impl.leave + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.spaces.LeaveSpaceHandle +import io.element.android.libraries.matrix.api.spaces.LeaveSpaceRoom +import io.element.android.libraries.matrix.api.spaces.SpaceRoom +import io.element.android.libraries.matrix.test.AN_EXCEPTION +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_ROOM_ID_3 +import io.element.android.libraries.matrix.test.A_SPACE_ID +import io.element.android.libraries.matrix.test.A_SPACE_NAME +import io.element.android.libraries.matrix.test.spaces.FakeLeaveSpaceHandle +import io.element.android.libraries.previewutils.room.aSpaceRoom +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value +import io.element.android.tests.testutils.test +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class LeaveSpacePresenterTest { + private val aSpace = aSpaceRoom( + roomId = A_SPACE_ID, + displayName = A_SPACE_NAME, + ) + + @Test + fun `present - initial state`() = runTest { + val presenter = createLeaveSpacePresenter( + leaveSpaceHandle = FakeLeaveSpaceHandle( + roomsResult = { Result.success(emptyList()) }, + ), + ) + presenter.test { + val state = awaitItem() + assertThat(state.spaceName).isNull() + assertThat(state.isLastAdmin).isFalse() + assertThat(state.selectableSpaceRooms.isLoading()).isTrue() + assertThat(state.leaveSpaceAction).isEqualTo(AsyncAction.Uninitialized) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - fail to load rooms`() = runTest { + val presenter = createLeaveSpacePresenter( + leaveSpaceHandle = FakeLeaveSpaceHandle( + roomsResult = { Result.failure(AN_EXCEPTION) }, + ) + ) + presenter.test { + val state = awaitItem() + assertThat(state.selectableSpaceRooms.isLoading()).isTrue() + assertThat(state.leaveSpaceAction).isEqualTo(AsyncAction.Uninitialized) + skipItems(3) + val stateError = awaitItem() + assertThat(stateError.selectableSpaceRooms.isFailure()).isTrue() + // Retry + stateError.eventSink(LeaveSpaceEvents.Retry) + skipItems(1) + val stateLoadingAgain = awaitItem() + assertThat(stateLoadingAgain.selectableSpaceRooms.isLoading()).isTrue() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - current space name and is last admin`() = runTest { + val presenter = createLeaveSpacePresenter( + leaveSpaceHandle = FakeLeaveSpaceHandle( + roomsResult = { Result.success(listOf(aLeaveSpaceRoom(spaceRoom = aSpace, isLastAdmin = true))) }, + ) + ) + presenter.test { + val state = awaitItem() + assertThat(state.spaceName).isNull() + skipItems(3) + val finalState = awaitItem() + assertThat(finalState.spaceName).isEqualTo(A_SPACE_NAME) + assertThat(finalState.isLastAdmin).isTrue() + // The current state is not in the sub room list + assertThat(finalState.selectableSpaceRooms.dataOrNull()!!).isEmpty() + } + } + + @Test + fun `present - direct rooms are filtered out`() = runTest { + val leaveResult = lambdaRecorder, Result> { Result.success(Unit) } + val presenter = createLeaveSpacePresenter( + leaveSpaceHandle = FakeLeaveSpaceHandle( + roomsResult = { + Result.success( + listOf( + aLeaveSpaceRoom(spaceRoom = aSpace), + aLeaveSpaceRoom( + spaceRoom = aSpaceRoom(roomId = A_ROOM_ID, isDirect = false) + ), + aLeaveSpaceRoom( + spaceRoom = aSpaceRoom(roomId = A_ROOM_ID_2, isDirect = true) + ), + aLeaveSpaceRoom( + spaceRoom = aSpaceRoom(roomId = A_ROOM_ID_3, isDirect = null) + ), + ) + ) + }, + leaveResult = leaveResult, + ) + ) + presenter.test { + val state = awaitItem() + assertThat(state.spaceName).isNull() + skipItems(3) + val finalState = awaitItem() + // The current state is not in the sub room list + 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) + val stateLeaving = awaitItem() + assertThat(stateLeaving.leaveSpaceAction).isEqualTo(AsyncAction.Loading) + val stateLeft = awaitItem() + assertThat(stateLeft.leaveSpaceAction.isSuccess()).isTrue() + leaveResult.assertions().isCalledOnce().with( + value(listOf(A_ROOM_ID, A_ROOM_ID_3)) + ) + } + } + + @Test + fun `present - leave space and sub rooms`() = runTest { + val leaveResult = lambdaRecorder, Result> { Result.success(Unit) } + val presenter = createLeaveSpacePresenter( + leaveSpaceHandle = FakeLeaveSpaceHandle( + roomsResult = { + Result.success( + listOf( + LeaveSpaceRoom(aSpaceRoom(roomId = A_ROOM_ID), isLastAdmin = false), + LeaveSpaceRoom(aSpaceRoom(roomId = A_ROOM_ID_2), isLastAdmin = true), + ) + ) + }, + leaveResult = leaveResult, + ) + ) + presenter.test { + skipItems(4) + val state = awaitItem() + assertThat(state.spaceName).isNull() + assertThat(state.isLastAdmin).isFalse() + val data = state.selectableSpaceRooms.dataOrNull()!! + assertThat(data.size).isEqualTo(2) + // Only one room is selectable as the user is the last admin in the other one + val room1 = data[0] + assertThat(room1.spaceRoom.roomId).isEqualTo(A_ROOM_ID) + assertThat(room1.isSelected).isTrue() + assertThat(room1.isLastAdmin).isFalse() + val room2 = data[1] + assertThat(room2.spaceRoom.roomId).isEqualTo(A_ROOM_ID_2) + assertThat(room2.isSelected).isFalse() + assertThat(room2.isLastAdmin).isTrue() + // Deselect all + state.eventSink(LeaveSpaceEvents.DeselectAllRooms) + skipItems(1) + val stateAllDeselected = awaitItem() + val dataAllDeselected = stateAllDeselected.selectableSpaceRooms.dataOrNull()!! + assertThat(dataAllDeselected.any { it.isSelected }).isFalse() + // Select all + stateAllDeselected.eventSink(LeaveSpaceEvents.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)) + 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)) + skipItems(1) + val stateOneSelected = awaitItem() + val dataOneSelected = stateOneSelected.selectableSpaceRooms.dataOrNull()!! + assertThat(dataOneSelected[0].isSelected).isTrue() + // Leave space + stateOneSelected.eventSink(LeaveSpaceEvents.LeaveSpace) + val stateLeaving = awaitItem() + assertThat(stateLeaving.leaveSpaceAction).isEqualTo(AsyncAction.Loading) + val stateLeft = awaitItem() + assertThat(stateLeft.leaveSpaceAction.isSuccess()).isTrue() + leaveResult.assertions().isCalledOnce().with( + value(listOf(A_ROOM_ID)) + ) + } + } + + @Test + fun `present - leave space error and close`() = runTest { + val leaveResult = lambdaRecorder, Result> { + Result.failure(AN_EXCEPTION) + } + val presenter = createLeaveSpacePresenter( + leaveSpaceHandle = FakeLeaveSpaceHandle( + roomsResult = { Result.success(emptyList()) }, + leaveResult = leaveResult, + ) + ) + presenter.test { + skipItems(4) + val state = awaitItem() + state.eventSink(LeaveSpaceEvents.LeaveSpace) + val stateLeaving = awaitItem() + assertThat(stateLeaving.leaveSpaceAction).isEqualTo(AsyncAction.Loading) + val stateError = awaitItem() + assertThat(stateError.leaveSpaceAction.isFailure()).isTrue() + // Close error + stateError.eventSink(LeaveSpaceEvents.CloseError) + val stateErrorClosed = awaitItem() + assertThat(stateErrorClosed.leaveSpaceAction).isEqualTo(AsyncAction.Uninitialized) + } + } + + private fun createLeaveSpacePresenter( + leaveSpaceHandle: LeaveSpaceHandle = FakeLeaveSpaceHandle(), + ): LeaveSpacePresenter { + return LeaveSpacePresenter( + leaveSpaceHandle = leaveSpaceHandle, + ) + } +} + +private fun aLeaveSpaceRoom( + spaceRoom: SpaceRoom = aSpaceRoom( + roomId = A_SPACE_ID, + displayName = A_SPACE_NAME, + ), + isLastAdmin: Boolean = false, +) = LeaveSpaceRoom( + spaceRoom = spaceRoom, + isLastAdmin = isLastAdmin, +) diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceStateTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceStateTest.kt new file mode 100644 index 0000000000..998868593f --- /dev/null +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceStateTest.kt @@ -0,0 +1,128 @@ +/* + * Copyright 2025 New Vector 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.space.impl.leave + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.architecture.AsyncData +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList +import org.junit.Test + +class LeaveSpaceStateTest { + @Test + fun `test loading`() { + val sut = aLeaveSpaceState( + selectableSpaceRooms = AsyncData.Loading() + ) + assertThat(sut.showQuickAction).isFalse() + assertThat(sut.showLeaveButton).isFalse() + assertThat(sut.areAllSelected).isTrue() + assertThat(sut.hasOnlyLastAdminRoom).isFalse() + assertThat(sut.selectedRoomsCount).isEqualTo(0) + } + + @Test + fun `test no rooms`() { + val sut = aLeaveSpaceState( + selectableSpaceRooms = AsyncData.Success( + persistentListOf() + ) + ) + assertThat(sut.showQuickAction).isFalse() + assertThat(sut.showLeaveButton).isTrue() + assertThat(sut.areAllSelected).isTrue() + assertThat(sut.hasOnlyLastAdminRoom).isFalse() + assertThat(sut.selectedRoomsCount).isEqualTo(0) + } + + @Test + fun `test last admin`() { + val sut = aLeaveSpaceState( + isLastAdmin = true, + selectableSpaceRooms = AsyncData.Success( + persistentListOf( + aSelectableSpaceRoom(isLastAdmin = false, isSelected = false), + ) + ) + ) + assertThat(sut.showQuickAction).isFalse() + assertThat(sut.showLeaveButton).isFalse() + assertThat(sut.areAllSelected).isFalse() + assertThat(sut.hasOnlyLastAdminRoom).isFalse() + assertThat(sut.selectedRoomsCount).isEqualTo(0) + } + + @Test + fun `test no last admin, 1 selected, 1 not selected`() { + val sut = aLeaveSpaceState( + selectableSpaceRooms = AsyncData.Success( + listOf( + aSelectableSpaceRoom(isLastAdmin = false, isSelected = true), + aSelectableSpaceRoom(isLastAdmin = false, isSelected = false), + ).toImmutableList() + ) + ) + assertThat(sut.showQuickAction).isTrue() + assertThat(sut.showLeaveButton).isTrue() + assertThat(sut.areAllSelected).isFalse() + assertThat(sut.hasOnlyLastAdminRoom).isFalse() + assertThat(sut.selectedRoomsCount).isEqualTo(1) + } + + @Test + fun `test no last admin, 2 selected`() { + val sut = aLeaveSpaceState( + selectableSpaceRooms = AsyncData.Success( + listOf( + aSelectableSpaceRoom(isLastAdmin = false, isSelected = true), + aSelectableSpaceRoom(isLastAdmin = false, isSelected = true), + ).toImmutableList() + ) + ) + assertThat(sut.showQuickAction).isTrue() + assertThat(sut.showLeaveButton).isTrue() + assertThat(sut.areAllSelected).isTrue() + assertThat(sut.hasOnlyLastAdminRoom).isFalse() + assertThat(sut.selectedRoomsCount).isEqualTo(2) + } + + @Test + fun `test 1 last admin, 2 selected`() { + val sut = aLeaveSpaceState( + selectableSpaceRooms = AsyncData.Success( + persistentListOf( + aSelectableSpaceRoom(isLastAdmin = true, isSelected = false), + aSelectableSpaceRoom(isLastAdmin = false, isSelected = true), + aSelectableSpaceRoom(isLastAdmin = false, isSelected = true), + ) + ) + ) + assertThat(sut.showQuickAction).isTrue() + assertThat(sut.showLeaveButton).isTrue() + assertThat(sut.areAllSelected).isTrue() + assertThat(sut.hasOnlyLastAdminRoom).isFalse() + assertThat(sut.selectedRoomsCount).isEqualTo(2) + } + + @Test + fun `test only last admin`() { + val sut = aLeaveSpaceState( + selectableSpaceRooms = AsyncData.Success( + listOf( + aSelectableSpaceRoom(isLastAdmin = true, isSelected = false), + aSelectableSpaceRoom(isLastAdmin = true, isSelected = false), + ).toImmutableList() + ) + ) + assertThat(sut.showQuickAction).isFalse() + assertThat(sut.showLeaveButton).isTrue() + assertThat(sut.areAllSelected).isTrue() + assertThat(sut.hasOnlyLastAdminRoom).isTrue() + assertThat(sut.selectedRoomsCount).isEqualTo(0) + } +} 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 new file mode 100644 index 0000000000..fbef7bb3a1 --- /dev/null +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt @@ -0,0 +1,340 @@ +/* + * Copyright 2025 New Vector 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(ExperimentalCoroutinesApi::class) + +package io.element.android.features.space.impl.root + +import com.google.common.truth.Truth.assertThat +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.AcceptDeclineInviteState +import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInviteState +import io.element.android.features.invite.api.toInviteData +import io.element.android.features.invite.test.InMemorySeenInvitesStore +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias +import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias +import io.element.android.libraries.matrix.api.room.CurrentUserMembership +import io.element.android.libraries.matrix.api.room.join.JoinRoom +import io.element.android.libraries.matrix.api.spaces.SpaceRoomList +import io.element.android.libraries.matrix.test.AN_EXCEPTION +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.FakeMatrixClient +import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom +import io.element.android.libraries.matrix.test.spaces.FakeSpaceRoomList +import io.element.android.libraries.previewutils.room.aSpaceRoom +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value +import io.element.android.tests.testutils.test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import im.vector.app.features.analytics.plan.JoinedRoom as AnalyticsJoinedRoom + +class SpacePresenterTest { + @Test + fun `present - initial state`() = runTest { + val paginateResult = lambdaRecorder> { + Result.success(Unit) + } + val spaceRoomList = FakeSpaceRoomList(paginateResult = paginateResult) + val presenter = createSpacePresenter(spaceRoomList = spaceRoomList) + presenter.test { + val state = awaitItem() + assertThat(state.currentSpace).isNull() + assertThat(state.children).isEmpty() + assertThat(state.seenSpaceInvites).isEmpty() + assertThat(state.hideInvitesAvatar).isFalse() + assertThat(state.hasMoreToLoad).isTrue() + assertThat(state.joinActions).isEmpty() + assertThat(state.acceptDeclineInviteState).isEqualTo(anAcceptDeclineInviteState()) + assertThat(state.topicViewerState).isEqualTo(TopicViewerState.Hidden) + advanceUntilIdle() + paginateResult.assertions().isCalledOnce() + } + } + + @Test + fun `present - load more`() = runTest { + val paginateResult = lambdaRecorder> { + Result.success(Unit) + } + val spaceRoomList = FakeSpaceRoomList(paginateResult = paginateResult) + val presenter = createSpacePresenter(spaceRoomList = spaceRoomList) + presenter.test { + val state = awaitItem() + advanceUntilIdle() + paginateResult.assertions().isCalledOnce() + state.eventSink(SpaceEvents.LoadMore) + advanceUntilIdle() + paginateResult.assertions().isCalledExactly(2) + } + } + + @Test + fun `present - has more to load value`() = runTest { + val paginateResult = lambdaRecorder> { + Result.success(Unit) + } + val spaceRoomList = FakeSpaceRoomList(paginateResult = paginateResult) + val presenter = createSpacePresenter(spaceRoomList = spaceRoomList) + presenter.test { + val state = awaitItem() + advanceUntilIdle() + assertThat(state.hasMoreToLoad).isTrue() + spaceRoomList.emitPaginationStatus( + SpaceRoomList.PaginationStatus.Idle(hasMoreToLoad = false) + ) + assertThat(awaitItem().hasMoreToLoad).isFalse() + spaceRoomList.emitPaginationStatus( + SpaceRoomList.PaginationStatus.Idle(hasMoreToLoad = true) + ) + assertThat(awaitItem().hasMoreToLoad).isTrue() + } + } + + @Test + fun `present - current space value`() = runTest { + val paginateResult = lambdaRecorder> { + Result.success(Unit) + } + val spaceRoomList = FakeSpaceRoomList(paginateResult = paginateResult) + val presenter = createSpacePresenter(spaceRoomList = spaceRoomList) + presenter.test { + val state = awaitItem() + advanceUntilIdle() + assertThat(state.currentSpace).isNull() + val aSpace = aSpaceRoom() + spaceRoomList.emitCurrentSpace(aSpace) + assertThat(awaitItem().currentSpace).isEqualTo(aSpace) + } + } + + @Test + fun `present - children value`() = runTest { + val paginateResult = lambdaRecorder> { + Result.success(Unit) + } + val spaceRoomList = FakeSpaceRoomList(paginateResult = paginateResult) + val presenter = createSpacePresenter(spaceRoomList = spaceRoomList) + presenter.test { + val state = awaitItem() + advanceUntilIdle() + assertThat(state.children).isEmpty() + val aSpace = aSpaceRoom() + spaceRoomList.emitSpaceRooms(listOf(aSpace)) + assertThat(awaitItem().children).containsExactly(aSpace) + } + } + + @Test + fun `present - join a room success`() = runTest { + val joinRoom = lambdaRecorder, AnalyticsJoinedRoom.Trigger, Result> { _, _, _ -> + Result.success(Unit) + } + val serverNames = listOf("via1", "via2") + val aNotJoinedRoom = aSpaceRoom( + roomId = A_ROOM_ID_2, + via = serverNames, + state = null, + ) + val fakeSpaceRoomList = FakeSpaceRoomList( + initialSpaceRoomsValue = listOf( + aSpaceRoom( + roomId = A_ROOM_ID, + state = CurrentUserMembership.JOINED, + ), + aNotJoinedRoom, + ), + paginateResult = { Result.success(Unit) }, + ) + val presenter = createSpacePresenter( + spaceRoomList = fakeSpaceRoomList, + joinRoom = FakeJoinRoom( + lambda = joinRoom, + ), + ) + presenter.test { + skipItems(1) + val state = awaitItem() + assertThat(state.joinActions[A_ROOM_ID_2]).isNull() + state.eventSink(SpaceEvents.Join(aNotJoinedRoom)) + val joiningState = awaitItem() + assertThat(joiningState.joinActions[A_ROOM_ID_2]).isEqualTo(AsyncAction.Loading) + // Let the joinRoom call complete + advanceUntilIdle() + runCurrent() + // The room is joined + fakeSpaceRoomList.emitSpaceRooms( + listOf( + aSpaceRoom( + roomId = A_ROOM_ID, + state = CurrentUserMembership.JOINED, + ), + aNotJoinedRoom.copy(state = CurrentUserMembership.JOINED), + ) + ) + skipItems(1) + val joinedState = awaitItem() + // Joined room is removed from the join actions + assertThat(joinedState.joinActions).doesNotContainKey(A_ROOM_ID_2) + joinRoom.assertions().isCalledOnce().with( + value(A_ROOM_ID_2.toRoomIdOrAlias()), + value(serverNames), + value(AnalyticsJoinedRoom.Trigger.SpaceHierarchy), + ) + } + } + + @Test + fun `present - join a room failure`() = runTest { + val aNotJoinedRoom = aSpaceRoom( + roomId = A_ROOM_ID_2, + state = null, + ) + val fakeSpaceRoomList = FakeSpaceRoomList( + initialSpaceRoomsValue = listOf( + aSpaceRoom( + roomId = A_ROOM_ID, + state = CurrentUserMembership.JOINED, + ), + aNotJoinedRoom, + ), + paginateResult = { Result.success(Unit) }, + ) + val presenter = createSpacePresenter( + spaceRoomList = fakeSpaceRoomList, + joinRoom = FakeJoinRoom( + lambda = { _, _, _ -> Result.failure(AN_EXCEPTION) }, + ), + ) + presenter.test { + skipItems(1) + val state = awaitItem() + assertThat(state.joinActions[A_ROOM_ID_2]).isNull() + state.eventSink(SpaceEvents.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) + val clearedState = awaitItem() + assertThat(clearedState.joinActions[A_ROOM_ID_2]).isEqualTo(AsyncAction.Uninitialized) + } + } + + @Test + fun `present - topic viewer state`() = runTest { + val paginateResult = lambdaRecorder> { + Result.success(Unit) + } + val spaceRoomList = FakeSpaceRoomList(paginateResult = paginateResult) + val presenter = createSpacePresenter(spaceRoomList = spaceRoomList) + presenter.test { + val state = awaitItem() + assertThat(state.topicViewerState).isEqualTo(TopicViewerState.Hidden) + advanceUntilIdle() + state.eventSink(SpaceEvents.ShowTopicViewer("topic")) + assertThat(awaitItem().topicViewerState).isEqualTo(TopicViewerState.Shown("topic")) + state.eventSink(SpaceEvents.HideTopicViewer) + assertThat(awaitItem().topicViewerState).isEqualTo(TopicViewerState.Hidden) + } + } + + @Test + fun `present - accept invite is transmitted to acceptDeclineInviteState`() { + `invite action is transmitted to acceptDeclineInviteState`( + acceptInvite = true, + ) + } + + @Test + fun `present - decline invite is transmitted to acceptDeclineInviteState`() { + `invite action is transmitted to acceptDeclineInviteState`( + acceptInvite = false, + ) + } + + private fun `invite action is transmitted to acceptDeclineInviteState`( + acceptInvite: Boolean, + ) = runTest { + val eventRecorder = EventsRecorder() + val anInvitedRoom = aSpaceRoom( + roomId = A_ROOM_ID_2, + state = CurrentUserMembership.INVITED, + ) + val fakeSpaceRoomList = FakeSpaceRoomList( + initialSpaceRoomsValue = listOf( + aSpaceRoom( + roomId = A_ROOM_ID, + state = CurrentUserMembership.JOINED, + ), + anInvitedRoom, + ), + paginateResult = { Result.success(Unit) }, + ) + val presenter = createSpacePresenter( + spaceRoomList = fakeSpaceRoomList, + acceptDeclineInvitePresenter = { + anAcceptDeclineInviteState( + eventSink = eventRecorder, + ) + }, + ) + presenter.test { + skipItems(1) + val state = awaitItem() + assertThat(state.joinActions[A_ROOM_ID_2]).isNull() + if (acceptInvite) { + state.eventSink(SpaceEvents.AcceptInvite(anInvitedRoom)) + eventRecorder.assertSingle( + AcceptDeclineInviteEvents.AcceptInvite( + invite = anInvitedRoom.toInviteData(), + ) + ) + } else { + state.eventSink(SpaceEvents.DeclineInvite(anInvitedRoom)) + eventRecorder.assertSingle( + AcceptDeclineInviteEvents.DeclineInvite( + invite = anInvitedRoom.toInviteData(), + shouldConfirm = true, + blockUser = false, + ) + ) + } + } + } + + private fun TestScope.createSpacePresenter( + client: MatrixClient = FakeMatrixClient(), + spaceRoomList: SpaceRoomList = FakeSpaceRoomList(), + seenInvitesStore: SeenInvitesStore = InMemorySeenInvitesStore(), + joinRoom: JoinRoom = FakeJoinRoom( + lambda = { _, _, _ -> Result.success(Unit) }, + ), + acceptDeclineInvitePresenter: Presenter = Presenter { anAcceptDeclineInviteState() }, + ): SpacePresenter { + return SpacePresenter( + client = client, + spaceRoomList = spaceRoomList, + seenInvitesStore = seenInvitesStore, + joinRoom = joinRoom, + acceptDeclineInvitePresenter = acceptDeclineInvitePresenter, + sessionCoroutineScope = backgroundScope, + ) + } +} diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceStateTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceStateTest.kt new file mode 100644 index 0000000000..d036d7023c --- /dev/null +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceStateTest.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2025 New Vector 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.space.impl.root + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.test.AN_EXCEPTION +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_ROOM_ID_3 +import org.junit.Test + +class SpaceStateTest { + @Test + fun `test default state`() { + val state = aSpaceState() + assertThat(state.hasAnyFailure).isFalse() + assertThat(state.isJoining(A_ROOM_ID)).isFalse() + } + + @Test + fun `test has failure`() { + val state = aSpaceState( + joinActions = mapOf( + A_ROOM_ID to AsyncAction.Uninitialized, + A_ROOM_ID_2 to AsyncAction.Failure(AN_EXCEPTION), + A_ROOM_ID_3 to AsyncAction.Success(Unit), + ) + ) + assertThat(state.hasAnyFailure).isTrue() + } + + @Test + fun `test isJoining`() { + val state = aSpaceState( + joinActions = mapOf( + A_ROOM_ID to AsyncAction.Loading, + ) + ) + assertThat(state.isJoining(A_ROOM_ID)).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 new file mode 100644 index 0000000000..2702133780 --- /dev/null +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt @@ -0,0 +1,154 @@ +/* + * Copyright 2025 New Vector 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.space.impl.root + +import androidx.activity.ComponentActivity +import androidx.compose.runtime.Composable +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.libraries.matrix.api.room.CurrentUserMembership +import io.element.android.libraries.matrix.api.spaces.SpaceRoom +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_ROOM_NAME +import io.element.android.libraries.matrix.test.A_ROOM_TOPIC +import io.element.android.libraries.previewutils.room.aSpaceRoom +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.tests.testutils.EnsureNeverCalled +import io.element.android.tests.testutils.EnsureNeverCalledWithParam +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.clickOn +import io.element.android.tests.testutils.ensureCalledOnce +import io.element.android.tests.testutils.ensureCalledOnceWithParam +import io.element.android.tests.testutils.pressBack +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestRule +import org.junit.runner.RunWith +import org.robolectric.annotation.Config + +@RunWith(AndroidJUnit4::class) +class SpaceViewTest { + @get:Rule val rule = createAndroidComposeRule() + + @Test + fun `clicking on back invokes the expected callback`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + ensureCalledOnce { + rule.setSpaceView( + aSpaceState( + hasMoreToLoad = false, + eventSink = eventsRecorder, + ), + onBackClick = it, + ) + rule.pressBack() + } + } + + @Test + fun `clicking on a room name invokes the expected callback`() { + val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, displayName = A_ROOM_NAME) + val eventsRecorder = EventsRecorder(expectEvents = false) + ensureCalledOnceWithParam(aSpaceRoom) { + rule.setSpaceView( + aSpaceState( + children = listOf(aSpaceRoom), + hasMoreToLoad = false, + eventSink = eventsRecorder, + ), + onRoomClick = it, + ) + rule.onNodeWithText(A_ROOM_NAME).performClick() + } + } + + @Test + fun `clicking on Join room emits the expected Event`() { + val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, state = null) + val eventsRecorder = EventsRecorder() + rule.setSpaceView( + aSpaceState( + children = listOf(aSpaceRoom), + hasMoreToLoad = false, + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.action_join) + eventsRecorder.assertSingle(SpaceEvents.Join(aSpaceRoom)) + } + + @Config(qualifiers = "h1024dp") + @Test + fun `clicking on accept invite emits the expected Event`() { + val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, state = CurrentUserMembership.INVITED) + val eventsRecorder = EventsRecorder() + rule.setSpaceView( + aSpaceState( + hasMoreToLoad = false, + children = listOf(aSpaceRoom), + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.action_accept) + eventsRecorder.assertSingle(SpaceEvents.AcceptInvite(aSpaceRoom)) + } + + @Config(qualifiers = "h1024dp") + @Test + fun `clicking on decline invite emits the expected Event`() { + val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, state = CurrentUserMembership.INVITED) + val eventsRecorder = EventsRecorder() + rule.setSpaceView( + aSpaceState( + hasMoreToLoad = false, + children = listOf(aSpaceRoom), + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.action_decline) + eventsRecorder.assertSingle(SpaceEvents.DeclineInvite(aSpaceRoom)) + } + + @Config(qualifiers = "h1024dp") + @Test + fun `clicking on topic emits the expected Event`() { + val eventsRecorder = EventsRecorder() + rule.setSpaceView( + aSpaceState( + parentSpace = aSpaceRoom(topic = A_ROOM_TOPIC), + hasMoreToLoad = false, + eventSink = eventsRecorder, + ) + ) + rule.onNodeWithText(A_ROOM_TOPIC).performClick() + eventsRecorder.assertSingle(SpaceEvents.ShowTopicViewer(A_ROOM_TOPIC)) + } +} + +private fun AndroidComposeTestRule.setSpaceView( + state: SpaceState, + onBackClick: () -> Unit = EnsureNeverCalled(), + onRoomClick: (SpaceRoom) -> Unit = EnsureNeverCalledWithParam(), + onShareSpace: () -> Unit = EnsureNeverCalled(), + onLeaveSpaceClick: () -> Unit = EnsureNeverCalled(), + acceptDeclineInviteView: @Composable () -> Unit = {}, +) { + setContent { + SpaceView( + state = state, + onBackClick = onBackClick, + onRoomClick = onRoomClick, + onShareSpace = onShareSpace, + onLeaveSpaceClick = onLeaveSpaceClick, + acceptDeclineInviteView = acceptDeclineInviteView, + ) + } +} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatFlowNode.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatFlowNode.kt index 59c8e5bacb..30d1f3a2cf 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatFlowNode.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatFlowNode.kt @@ -19,7 +19,7 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.createroom.api.CreateRoomEntryPoint import io.element.android.features.startchat.DefaultStartChatNavigator @@ -36,7 +36,7 @@ import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class StartChatFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressNode.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressNode.kt index cb3a1766fa..101958ddad 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressNode.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressNode.kt @@ -14,13 +14,13 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.startchat.StartChatNavigator import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class JoinRoomByAddressNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt index 42bd54e9f2..540c1a4784 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt @@ -17,7 +17,7 @@ import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.features.startchat.StartChatNavigator import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.data.tryOrNull @@ -31,7 +31,7 @@ import kotlin.time.Duration.Companion.seconds private const val ADDRESS_RESOLVE_TIMEOUT_IN_SECONDS = 10 -@Inject +@AssistedInject class JoinRoomByAddressPresenter( @Assisted private val navigator: StartChatNavigator, private val client: MatrixClient, diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatNode.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatNode.kt index e5c8f04bbd..9a9ca85160 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatNode.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatNode.kt @@ -17,7 +17,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode import io.element.android.features.startchat.StartChatNavigator @@ -27,7 +27,7 @@ import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.services.analytics.api.AnalyticsService @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class StartChatNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 17f5076caf..38d15f6de3 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 @@ -17,8 +17,8 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.ContributesBinding -import dev.zacsweers.metro.Inject import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient @@ -31,7 +31,7 @@ import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -@Inject +@AssistedInject class DefaultUserListPresenter( @Assisted val args: UserListPresenterArgs, @Assisted val userRepository: UserRepository, diff --git a/features/startchat/impl/src/main/res/values-bg/translations.xml b/features/startchat/impl/src/main/res/values-bg/translations.xml index 21ad117fb6..113ca0b71e 100644 --- a/features/startchat/impl/src/main/res/values-bg/translations.xml +++ b/features/startchat/impl/src/main/res/values-bg/translations.xml @@ -1,6 +1,7 @@ "Нова стая" + "Възникна грешка при опита за започване на чат" "Присъединяване към стая по адрес" "Не е валиден адрес" "Въведете…" diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt index 77e9959dc5..5828d60c25 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt @@ -18,7 +18,7 @@ import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.call.api.CallType import io.element.android.features.call.api.ElementCallEntryPoint @@ -33,19 +33,19 @@ import io.element.android.libraries.architecture.inputs 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.core.SessionId import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder import io.element.android.libraries.matrix.api.verification.VerificationRequest import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class UserProfileFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, private val elementCallEntryPoint: ElementCallEntryPoint, - private val sessionIdHolder: CurrentSessionIdHolder, + private val sessionId: SessionId, private val mediaViewerEntryPoint: MediaViewerEntryPoint, private val outgoingVerificationEntryPoint: OutgoingVerificationEntryPoint, ) : BaseFlowNode( @@ -82,7 +82,7 @@ class UserProfileFlowNode( } override fun onStartCall(dmRoomId: RoomId) { - elementCallEntryPoint.startCall(CallType.RoomCall(sessionId = sessionIdHolder.current, roomId = dmRoomId)) + elementCallEntryPoint.startCall(CallType.RoomCall(sessionId = sessionId, roomId = dmRoomId)) } override fun onVerifyUser(userId: UserId) { @@ -99,7 +99,7 @@ class UserProfileFlowNode( } override fun onViewInTimeline(eventId: EventId) { - // Cannot happen + // Cannot happen } } mediaViewerEntryPoint.nodeBuilder(this, buildContext) diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfileNode.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfileNode.kt index 9a87ea1550..735957946a 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfileNode.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfileNode.kt @@ -15,7 +15,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode import io.element.android.features.userprofile.shared.UserProfileNodeHelper @@ -29,7 +29,7 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder import io.element.android.services.analytics.api.AnalyticsService @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class UserProfileNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 77786ca096..3f226d0213 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 @@ -19,7 +19,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +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 @@ -41,7 +41,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -@Inject +@AssistedInject class UserProfilePresenter( @Assisted private val userId: UserId, private val client: MatrixClient, diff --git a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileEntryPointTest.kt b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileEntryPointTest.kt index 1537167d20..75bc434048 100644 --- a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileEntryPointTest.kt +++ b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileEntryPointTest.kt @@ -19,9 +19,8 @@ import io.element.android.features.verifysession.api.OutgoingVerificationEntryPo import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder +import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_USER_ID -import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.node.TestParentNode @@ -43,7 +42,7 @@ class DefaultUserProfileEntryPointTest { UserProfileFlowNode( buildContext = buildContext, plugins = plugins, - sessionIdHolder = CurrentSessionIdHolder(FakeMatrixClient()), + sessionId = A_SESSION_ID, elementCallEntryPoint = object : ElementCallEntryPoint { override fun startCall(callType: CallType) = lambdaError() override suspend fun handleIncomingCall( diff --git a/features/userprofile/shared/src/main/res/values-bg/translations.xml b/features/userprofile/shared/src/main/res/values-bg/translations.xml index 677034496c..b2e8611d3d 100644 --- a/features/userprofile/shared/src/main/res/values-bg/translations.xml +++ b/features/userprofile/shared/src/main/res/values-bg/translations.xml @@ -1,13 +1,18 @@ "Блокиране" + "Блокираните потребители няма да могат да ви изпращат съобщения и всички техни съобщения ще бъдат скрити. Можете да ги отблокирате по всяко време." "Блокиране на потребителя" "Отблокиране" + "Ще можете да виждате отново всички съобщения от тях." "Отблокиране на потребителя" "Блокиране" + "Блокираните потребители няма да могат да ви изпращат съобщения и всички техни съобщения ще бъдат скрити. Можете да ги отблокирате по всяко време." "Блокиране на потребителя" "Профил" "Отблокиране" + "Ще можете да виждате отново всички съобщения от тях." "Отблокиране на потребителя" "Потвърждаване на %1$s" + "Възникна грешка при опита за започване на чат" diff --git a/features/userprofile/shared/src/main/res/values-hu/translations.xml b/features/userprofile/shared/src/main/res/values-hu/translations.xml index 0a433ee5da..eeccf83f4c 100644 --- a/features/userprofile/shared/src/main/res/values-hu/translations.xml +++ b/features/userprofile/shared/src/main/res/values-hu/translations.xml @@ -4,15 +4,15 @@ "A letiltott felhasználók nem fognak tudni üzeneteket küldeni, és az összes üzenetük rejtve lesz. Bármikor feloldhatja a letiltásukat." "Felhasználó letiltása" "Letiltás feloldása" - "Újra láthatja az összes üzenetét." - "Felhasználó kitiltásának feloldása" + "Újra látni fogja az összes üzenetét." + "Felhasználó letiltásának feloldása" "Letiltás" "A letiltott felhasználók nem fognak tudni üzeneteket küldeni, és az összes üzenetük rejtve lesz. Bármikor feloldhatja a letiltásukat." "Felhasználó letiltása" "Profil" "Letiltás feloldása" - "Újra láthatja az összes üzenetét." - "Felhasználó kitiltásának feloldása" + "Újra látni fogja az összes üzenetét." + "Felhasználó letiltásának feloldása" "Használja a webes alkalmazást a felhasználó ellenőrzéséhez." "A(z) %1$s ellenőrzése" "Hiba történt a csevegés indításakor" diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationNode.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationNode.kt index cc3d2f1e2d..a17054b9e6 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationNode.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationNode.kt @@ -14,14 +14,14 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class IncomingVerificationNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 8be176f117..176176a895 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 @@ -19,7 +19,7 @@ import androidx.compose.runtime.rememberCoroutineScope import com.freeletics.flowredux.compose.rememberStateAndDispatch import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.features.verifysession.impl.incoming.IncomingVerificationState.Step import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.dateformatter.api.DateFormatter @@ -38,7 +38,7 @@ import timber.log.Timber import io.element.android.features.verifysession.impl.incoming.IncomingVerificationStateMachine.Event as StateMachineEvent import io.element.android.features.verifysession.impl.incoming.IncomingVerificationStateMachine.State as StateMachineState -@Inject +@AssistedInject class IncomingVerificationPresenter( @Assisted private val verificationRequest: VerificationRequest.Incoming, @Assisted private val navigator: IncomingVerificationNavigator, @@ -155,7 +155,7 @@ class IncomingVerificationPresenter( StateMachineState.RejectingIncomingVerification, null -> { Step.Initial( - deviceDisplayName = sessionVerificationRequestDetails.senderProfile.displayName ?: sessionVerificationRequestDetails.deviceId.value, + deviceDisplayName = sessionVerificationRequestDetails.deviceDisplayName, deviceId = sessionVerificationRequestDetails.deviceId, formattedSignInTime = formattedSignInTime, isWaiting = machineState == StateMachineState.AcceptingIncomingVerification || 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 fdc9f373d4..00bffa4ce3 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 @@ -22,7 +22,7 @@ data class IncomingVerificationState( @Stable sealed interface Step { data class Initial( - val deviceDisplayName: String, + val deviceDisplayName: String?, val deviceId: DeviceId, val formattedSignInTime: String, val isWaiting: Boolean, diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateProvider.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateProvider.kt index cdad2669d6..478e696889 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateProvider.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateProvider.kt @@ -14,6 +14,7 @@ import io.element.android.features.verifysession.impl.ui.aEmojisSessionVerificat import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.FlowId import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails import io.element.android.libraries.matrix.api.verification.VerificationRequest @@ -55,26 +56,28 @@ internal fun aStepInitial( internal fun anIncomingSessionVerificationRequest() = VerificationRequest.Incoming.OtherSession( details = SessionVerificationRequestDetails( - senderProfile = SessionVerificationRequestDetails.SenderProfile( + senderProfile = MatrixUser( userId = UserId("@alice:example.com"), displayName = "Alice", avatarUrl = null, ), flowId = FlowId("1234"), deviceId = DeviceId("ILAKNDNASDLK"), + deviceDisplayName = "a device name", firstSeenTimestamp = 0, ) ) internal fun anIncomingUserVerificationRequest() = VerificationRequest.Incoming.User( details = SessionVerificationRequestDetails( - senderProfile = SessionVerificationRequestDetails.SenderProfile( + senderProfile = MatrixUser( userId = UserId("@alice:example.com"), displayName = "Alice", avatarUrl = null, ), flowId = FlowId("1234"), deviceId = DeviceId("ILAKNDNASDLK"), + deviceDisplayName = "a device name", firstSeenTimestamp = 0, ) ) 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 8813935d0b..4506dfe0c9 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 @@ -26,6 +26,7 @@ import androidx.compose.ui.semantics.focused import androidx.compose.ui.semantics.progressBarRangeInfo import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme @@ -214,9 +215,7 @@ private fun ContentInitial( .padding(top = 24.dp), ) { VerificationUserProfileContent( - userId = request.details.senderProfile.userId, - displayName = request.details.senderProfile.displayName, - avatarUrl = request.details.senderProfile.avatarUrl, + user = request.details.senderProfile, ) } } @@ -238,7 +237,7 @@ private fun IncomingVerificationBottomMenu( VerificationBottomMenu { Button( modifier = Modifier.fillMaxWidth(), - text = stringResource(CommonStrings.action_start), + text = stringResource(CommonStrings.action_start_verification), onClick = { eventSink(IncomingVerificationViewEvents.StartVerification) }, ) TextButton( @@ -292,3 +291,11 @@ internal fun IncomingVerificationViewPreview(@PreviewParameter(IncomingVerificat state = state, ) } + +@Preview +@Composable +internal fun IncomingVerificationViewA11yPreview() = ElementPreview { + IncomingVerificationView( + state = anIncomingVerificationState(), + ) +} diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/ui/SessionDetailsView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/ui/SessionDetailsView.kt index da3471ddc1..14cd6733d9 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/ui/SessionDetailsView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/ui/SessionDetailsView.kt @@ -34,7 +34,7 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun SessionDetailsView( - deviceName: String, + deviceName: String?, deviceId: DeviceId, signInFormattedTimestamp: String, modifier: Modifier = Modifier, @@ -61,7 +61,7 @@ fun SessionDetailsView( resourceId = CompoundDrawables.ic_compound_devices ) Text( - text = deviceName, + text = deviceName ?: deviceId.value, style = ElementTheme.typography.fontBodyMdMedium, color = ElementTheme.colors.textPrimary, ) @@ -87,9 +87,16 @@ fun SessionDetailsView( @PreviewsDayNight @Composable internal fun SessionDetailsViewPreview() = ElementPreview { - SessionDetailsView( - deviceName = "Element X Android", - deviceId = DeviceId("ILAKNDNASDLK"), - signInFormattedTimestamp = "12:34", - ) + Column { + SessionDetailsView( + deviceName = "Element X Android", + deviceId = DeviceId("ILAKNDNASDLK"), + signInFormattedTimestamp = "12:34", + ) + SessionDetailsView( + deviceName = null, + deviceId = DeviceId("ILAKNDNASDLK"), + signInFormattedTimestamp = "12:34", + ) + } } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationNode.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationNode.kt index c6e8b95230..9941ce58fe 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationNode.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationNode.kt @@ -14,14 +14,14 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class OutgoingVerificationNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 84ebec96de..c985c15e36 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 @@ -18,7 +18,7 @@ import androidx.compose.runtime.remember import com.freeletics.flowredux.compose.rememberStateAndDispatch import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.encryption.EncryptionService @@ -34,7 +34,7 @@ import timber.log.Timber import io.element.android.features.verifysession.impl.outgoing.OutgoingVerificationStateMachine.Event as StateMachineEvent import io.element.android.features.verifysession.impl.outgoing.OutgoingVerificationStateMachine.State as StateMachineState -@Inject +@AssistedInject class OutgoingVerificationPresenter( @Assisted private val showDeviceVerifiedScreen: Boolean, @Assisted private val verificationRequest: VerificationRequest.Outgoing, 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 53cfa97435..2357b39be7 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 @@ -157,7 +157,10 @@ private fun OutgoingVerificationHeader(step: Step, request: VerificationRequest. } Step.Canceled -> CommonStrings.common_verification_failed Step.Ready -> R.string.screen_session_verification_compare_emojis_title - Step.Completed -> CommonStrings.common_verification_complete + Step.Completed -> when (request) { + is VerificationRequest.Outgoing.CurrentSession -> R.string.screen_session_verification_device_verified + is VerificationRequest.Outgoing.User -> CommonStrings.common_verification_complete + } is Step.Verifying -> when (step.data) { is SessionVerificationData.Decimals -> R.string.screen_session_verification_compare_numbers_title is SessionVerificationData.Emojis -> R.string.screen_session_verification_compare_emojis_title diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationUserProfileContent.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationUserProfileContent.kt index 4f6cfcf456..2e17b736f2 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationUserProfileContent.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationUserProfileContent.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -23,7 +24,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.components.avatar.Avatar -import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.preview.ElementPreview @@ -31,18 +31,21 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.ui.model.getAvatarData +import io.element.android.libraries.matrix.ui.model.getBestName +/** + * Ref: https://www.figma.com/design/lMrKOhS8BEb75GXVq7FnNI/ER-96--User-Verification-by-Emoji?node-id=116-52049 + */ @Composable fun VerificationUserProfileContent( - userId: UserId, - displayName: String?, - avatarUrl: String?, + user: MatrixUser, modifier: Modifier = Modifier, ) { - val avatarData = remember(userId, displayName, avatarUrl) { - AvatarData(id = userId.value, name = displayName, url = avatarUrl, size = AvatarSize.UserVerification) + val avatarData = remember(user) { + user.getAvatarData(AvatarSize.UserVerification) } - Row( modifier = modifier .fillMaxWidth() @@ -55,12 +58,20 @@ fun VerificationUserProfileContent( avatarData = avatarData, avatarType = AvatarType.User, ) - Spacer(modifier = Modifier.padding(12.dp)) + Spacer(modifier = Modifier.width(12.dp)) Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { - Text(text = displayName ?: userId.value, style = ElementTheme.typography.fontBodyLgMedium, color = ElementTheme.colors.textPrimary) + Text( + text = user.getBestName(), + style = ElementTheme.typography.fontBodyLgMedium, + color = ElementTheme.colors.textPrimary, + ) - if (displayName != null) { - Text(text = userId.value, style = ElementTheme.typography.fontBodyMdRegular, color = ElementTheme.colors.textSecondary) + if (user.displayName.isNullOrEmpty().not()) { + Text( + text = user.userId.value, + style = ElementTheme.typography.fontBodyMdRegular, + color = ElementTheme.colors.textSecondary, + ) } } } @@ -72,8 +83,10 @@ internal fun VerificationUserProfileContentPreview() = ElementPreview( drawableFallbackForImages = CommonDrawables.sample_avatar ) { VerificationUserProfileContent( - userId = UserId("@alice:example.com"), - displayName = "Alice", - avatarUrl = "https://example.com/avatar.png", + user = MatrixUser( + userId = UserId("@alice:example.com"), + displayName = "Alice", + avatarUrl = "https://example.com/avatar.png", + ) ) } diff --git a/features/verifysession/impl/src/main/res/values-be/translations.xml b/features/verifysession/impl/src/main/res/values-be/translations.xml index c48da7e8d0..a11cb7d607 100644 --- a/features/verifysession/impl/src/main/res/values-be/translations.xml +++ b/features/verifysession/impl/src/main/res/values-be/translations.xml @@ -16,6 +16,7 @@ "Пераканайцеся, што прыведзеныя ніжэй лічбы супадаюць з лічбамі, паказанымі ў іншым сеансе." "Параўнайце лічбы" "Ваш новы сеанс пацверджаны. Ён мае доступ да вашых зашыфраваных паведамленняў, і іншыя карыстальнікі будуць лічыць яго давераным." + "Прылада праверана" "Увядзіце ключ аднаўлення" "Дакажыце, што гэта вы, каб атрымаць доступ да вашай зашыфраванай гісторыі паведамленняў." "Адкрыйце існуючы сеанс" @@ -24,6 +25,7 @@ "Чаканне супадзення" "Параўнайце ўнікальны набор эмодзі." "Параўнайце ўнікальныя эмодзі, пераканаўшыся, што яны размешчаны ў тым жа парадку." + "Ваш новы сеанс пацверджаны. Ён мае доступ да вашых зашыфраваных паведамленняў, і іншыя карыстальнікі будуць лічыць яго давераным." "Прылада праверана" "Яны не супадаюць" "Яны супадаюць" diff --git a/features/verifysession/impl/src/main/res/values-bg/translations.xml b/features/verifysession/impl/src/main/res/values-bg/translations.xml index ab0a15f4ed..1a2e194696 100644 --- a/features/verifysession/impl/src/main/res/values-bg/translations.xml +++ b/features/verifysession/impl/src/main/res/values-bg/translations.xml @@ -8,8 +8,10 @@ "Устройството е потвърдено" "Използване на друго устройство" "Нещо не изглежда наред. Или времето за изчакване на заявката е изтекло, или заявката е отхвърлена." - "Потвърдете, че емоджитата по-долу съвпадат с показаните в другата ви сесия." + "Потвърдете, че емоджитата по-долу съвпадат с показаните в другото ви устройство." "Сравнете емоджита" + "Сега можете да четете или изпращате съобщения сигурно на другото си устройство." + "Устройството е потвърдено" "Въвеждане на ключ за възстановяване" "Докажете, че сте вие, за да получите достъп до хронологията на шифрованите си съобщения." "Отворете съществуваща сесия" @@ -17,6 +19,7 @@ "Готов съм" "В очакване на съвпадение" "Сравнете уникален набор от емоджита." + "Сравнете уникалните емоджита, като се уверите, че се появяват в същия ред." "Неуспешно потвърждаване" "Сега можете да четете или изпращате съобщения сигурно на другото си устройство." "Устройството е потвърдено" @@ -24,7 +27,6 @@ "Те съвпадат" "Уверете се, че приложението е отворено на другото устройство, преди да започнете потвърждението оттук." "Отворете приложението на друго потвърдено устройство" - "Чака се другото устройство" "Чака се другият потребител" "След като бъдете приети, ще можете да продължите потвърждението." "Приемете заявката, за да започнете процеса на потвърждаване в другата си сесия, за да продължите." diff --git a/features/verifysession/impl/src/main/res/values-cs/translations.xml b/features/verifysession/impl/src/main/res/values-cs/translations.xml index bdf73c53f1..5d0b28bc0a 100644 --- a/features/verifysession/impl/src/main/res/values-cs/translations.xml +++ b/features/verifysession/impl/src/main/res/values-cs/translations.xml @@ -11,13 +11,14 @@ "Použít jiné zařízení" "Čekání na jiném zařízení…" "Něco není v pořádku. Buď vypršel časový limit požadavku, nebo byl požadavek zamítnut." - "Zkontrolujte, zda se níže uvedené emotikony shodují s emotikony zobrazenými na jiné relaci." + "Zkontrolujte, zda se níže uvedené emotikony shodují s emotikony zobrazenými na vašem druhém zařízení." "Porovnání emotikonů" "Zkontrolujte, zda se níže uvedené emotikony shodují s emotikony zobrazenými v zařízení druhého uživatele." "Potvrďte, že níže uvedená čísla odpovídají číslům zobrazeným na vaší druhé relaci." "Porovnejte čísla" - "Vaše nová relace je nyní ověřena. Má přístup k vašim zašifrovaným zprávám a ostatní uživatelé ji uvidí jako důvěryhodnou." + "Nyní můžete bezpečně číst nebo odesílat zprávy na svém druhém zařízení." "Nyní můžete důvěřovat identitě tohoto uživatele při odesílání nebo přijímání zpráv." + "Zařízení ověřeno" "Zadejte klíč pro obnovení" "Buď vypršel časový limit požadavku, požadavek byl zamítnut, nebo došlo k nesouladu ověření." "Pro přístup k historii zašifrovaných zpráv prokažte, že jste to vy." @@ -44,7 +45,7 @@ "Pro větší bezpečnost chce jiný uživatel ověřit vaši identitu. Zobrazí se vám sada emotikonů k porovnání." "Na druhém zařízení byste měli vidět vyskakovací okno. Začněte s ověrením tam." "Spusťte ověření na druhém zařízení" - "Čekání na druhé zařízení" + "Spusťte ověření na druhém zařízení" "Čekání na druhého uživatele" "Po přijetí budete moci pokračovat v ověřování." "Pro pokračování přijměte požadavek na zahájení ověření v jiné relaci." diff --git a/features/verifysession/impl/src/main/res/values-cy/translations.xml b/features/verifysession/impl/src/main/res/values-cy/translations.xml index eff8ac87d9..2b26322170 100644 --- a/features/verifysession/impl/src/main/res/values-cy/translations.xml +++ b/features/verifysession/impl/src/main/res/values-cy/translations.xml @@ -11,13 +11,14 @@ "Defnyddiwch ddyfais arall" "Yn aros ar ddyfais arall…" "Mae rhywbeth i weld o\'i le. Naill ai daeth y cais i ben neu cafodd y cais ei wrthod." - "Cadarnhewch fod yr emojis isod yn cyd-fynd â\'r rhai sy\'n cael eu dangos ar eich sesiwn arall." + "Cadarnhewch fod yr emojis isod yn cyd-fynd â\'r rhai sy\'n cael eu dangos ar eich dyfais arall." "Cymharwch emojis" "Cadarnhewch fod yr emojis isod yn cyd-fynd â\'r rhai sy\'n cael eu dangos ar ddyfais y defnyddiwr arall." "Cadarnhewch fod y rhifau isod yn cyfateb i\'r rhai sy\'n cael eu dangos ar eich sesiwn arall." "Cymharwch rifau" - "Mae eich sesiwn newydd bellach wedi\'i dilysu. Mae ganddo fynediad i\'ch negeseuon wedi\'u hamgryptio, a bydd defnyddwyr eraill yn ei ystyried yn rhai y mae modd ymddiried ynddyn nhw." + "Nawr gallwch chi ddarllen neu anfon negeseuon yn ddiogel ar eich dyfais arall." "Nawr gallwch ymddiried yn hunaniaeth y defnyddiwr hwn wrth anfon neu dderbyn negeseuon." + "Dyfais wedi\'i dilysu" "Rhowch eich allwedd adfer" "Naill ai daeth y cais i ben, gwrthodwyd y cais, neu roedd diffyg cyfatebiaeth dilysu." "Profwch mai chi sydd yno i gael mynediad at eich hanes negeseuon wedi\'u hamgryptio." @@ -44,7 +45,7 @@ "Er mwyn diogelwch ychwanegol, mae defnyddiwr arall eisiau gwirio pwy ydych chi. Bydd set o emojis yn cael eu dangos i chi eu cymharu." "Dylech weld llamlen ar y ddyfais arall. Dechreuwch y dilysiad o\'r fan honno nawr." "Cychwyn dilysu ar y ddyfais arall" - "Yn aros am eich dyfais arall" + "Cychwyn dilysu ar y ddyfais arall" "Yn aros am y defnyddiwr arall" "Unwaith y byddwch wedi\'ch derbyn, byddwch yn gallu parhau â\'r dilysu." "Derbyniwch y cais i gychwyn y broses ddilysu yn eich sesiwn arall i barhau." diff --git a/features/verifysession/impl/src/main/res/values-da/translations.xml b/features/verifysession/impl/src/main/res/values-da/translations.xml index a1586feff7..32cdf159d5 100644 --- a/features/verifysession/impl/src/main/res/values-da/translations.xml +++ b/features/verifysession/impl/src/main/res/values-da/translations.xml @@ -11,13 +11,14 @@ "Brug en anden enhed" "Venter på en anden enhed…" "Et ellervandet virker ikke rigtigt. Enten udløb anmodningen, eller anmodningen blev afvist." - "Bekræft, at emojierne nedenfor matcher dem, der vises på din anden session." + "Bekræft, at emojierne nedenfor matcher dem, der vises på din anden enhed." "Sammenlign emojier" "Bekræft, at emojierne nedenfor matcher dem, der vises på den anden brugers enhed." "Bekræft, at numrene nedenfor stemmer overens med dem, der vises på din anden session." "Sammenlign tal" - "Din nye session er nu bekræftet. Det har adgang til dine krypterede meddelelser, og andre brugere vil se den som betroet." + "Nu kan du læse eller sende beskeder sikkert med din anden enhed." "Nu kan du stole på identiteten af denne bruger, når I sender og modtager beskeder fra hinanden." + "Enhed verificeret" "Indtast gendannelsesnøgle" "Enten udløb anmodningen, den blev afvist, eller der var en fejl i verifikationen." "Bevis, at det er dig, for at få adgang til din krypterede beskedhistorik." @@ -32,7 +33,7 @@ "Verifikation mislykkedes" "Fortsæt kun, hvis du selv har startet denne verifikation." "Verificér den anden enhed for at holde din meddelelseshistorik sikker." - "Nu kan du læse eller sende beskeder sikkert på din anden enhed." + "Nu kan du læse eller sende beskeder sikkert med din anden enhed." "Enhed verificeret" "Anmodet om verifikation" "De matcher ikke" @@ -44,7 +45,7 @@ "For ekstra sikkerhed ønsker en anden bruger at bekræfte din identitet. Du får vist et sæt emojier til sammenligning." "Du burde se en popup på den anden enhed. Start verifikationen derfra nu." "Start verifikation på den anden enhed" - "Venter på den anden enhed" + "Start verifikation på den anden enhed" "Venter på den anden bruger" "Når du er blevet accepteret, kan du fortsætte med verifikationen." "Accepter anmodningen om at starte bekræftelsesprocessen i din anden session for at fortsætte." diff --git a/features/verifysession/impl/src/main/res/values-de/translations.xml b/features/verifysession/impl/src/main/res/values-de/translations.xml index 591d72e65d..377da35af3 100644 --- a/features/verifysession/impl/src/main/res/values-de/translations.xml +++ b/features/verifysession/impl/src/main/res/values-de/translations.xml @@ -11,13 +11,14 @@ "Ein anderes Gerät verwenden" "Bitte warten bis das andere Gerät bereit ist." "Etwas scheint nicht zu stimmen. Entweder ist das Zeitlimit für die Anfrage abgelaufen oder die Anfrage wurde abgelehnt." - "Bestätige, dass die folgenden Emojis mit denen in deiner anderen Sitzung übereinstimmen." + "Bestätige, dass die folgenden Emojis mit denen auf deinem anderen Gerät übereinstimmen." "Emojis vergleichen" "Bestätige, dass die folgenden Emojis mit denen auf dem Gerät des anderen Nutzers übereinstimmen." "Bestätige, dass die folgenden Zahlen mit denen in deiner anderen Sitzung übereinstimmen." "Vergleiche die Zahlen" - "Deine neue Sitzung ist nun verifiziert. Sie hat Zugriff auf deine verschlüsselten Nachrichten und wird von anderen Nutzern als vertrauenswürdig eingestuft." + "Jetzt kannst du verschlüsselte Nachrichten sicher auf deinem anderen Gerät schreiben und lesen." "Jetzt kannst du der Identität dieses Nutzers vertrauen, wenn du Nachrichten sendest oder empfängst." + "Gerät verifiziert" "Wiederherstellungsschlüssel eingeben" "Entweder ist die Anfrage abgelaufen, oder die Anfrage wurde abgelehnt, oder es gab eine Unstimmigkeit bei der Überprüfung." "Beweise deine Identität, um auf deinen verschlüsselten Nachrichtenverlauf zuzugreifen." @@ -32,7 +33,7 @@ "Verifizierung fehlgeschlagen" "Fahre nur fort, falls du diese Verifizierung selbst gestartet hast." "Verifiziere das andere Gerät, um deinen Nachrichtenverlauf sicher zu halten." - "Jetzt kannst du Nachrichten auf deinem anderen Gerät sicher lesen oder senden." + "Jetzt kannst du verschlüsselte Nachrichten sicher auf deinem anderen Gerät schreiben und lesen." "Gerät verifiziert" "Verifizierung angefordert" "Sie stimmen nicht überein" @@ -44,7 +45,7 @@ "Für zusätzliche Sicherheit möchte ein anderer Nutzer deine Identität verifizieren. Es werden dir einige Emojis zum Vergleich angezeigt." "Du solltest ein Popup-Fenster auf dem anderen Gerät sehen. Starte die Verifizierung von dort aus." "Starte die Verifizierung auf dem anderen Gerät" - "Warten auf das andere Gerät" + "Starte die Verifizierung auf dem anderen Gerät" "Warten auf den anderen Nutzer" "Nach der Bestätigung kannst du mit der Verifizierung fortfahren." "Akzeptiere die Anfrage für die Verifizierung in deiner anderen Sitzung um fortzufahren." diff --git a/features/verifysession/impl/src/main/res/values-el/translations.xml b/features/verifysession/impl/src/main/res/values-el/translations.xml index 74e926f73d..1cbb7d3c04 100644 --- a/features/verifysession/impl/src/main/res/values-el/translations.xml +++ b/features/verifysession/impl/src/main/res/values-el/translations.xml @@ -18,6 +18,7 @@ "Σύγκριση αριθμών" "Η νέα σου συνεδρία έχει πλέον επαληθευτεί. Έχει πρόσβαση στα κρυπτογραφημένα μηνύματά σας και άλλοι χρήστες θα το βλέπουν ως αξιόπιστο." "Τώρα μπορείτε να εμπιστευτείτε την ταυτότητα αυτού του χρήστη κατά την αποστολή ή τη λήψη μηνυμάτων." + "Επαληθευμένη συσκευή" "Εισαγωγή κλειδιού ανάκτησης" "Είτε το αίτημα έληξε είτε απορρίφθηκε είτε υπήρξε αναντιστοιχία επαλήθευσης." "Απέδειξε ότι είσαι εσύ για να αποκτήσεις πρόσβαση στο κρυπτογραφημένο ιστορικό μηνυμάτων σου." @@ -32,7 +33,7 @@ "Αποτυχία επαλήθευσης" "Συνέχισε μόνο εάν ξεκίνησες εσύ αυτήν την επαλήθευση." "Επαλήθευσε την άλλη συσκευή για να διατηρήσεις το ιστορικό μηνυμάτων σου ασφαλές." - "Τώρα μπορείς να διαβάσεις ή να στείλεις μηνύματα με ασφάλεια στην άλλη συσκευή σου." + "Η νέα σου συνεδρία έχει πλέον επαληθευτεί. Έχει πρόσβαση στα κρυπτογραφημένα μηνύματά σας και άλλοι χρήστες θα το βλέπουν ως αξιόπιστο." "Επαληθευμένη συσκευή" "Ζητήθηκε επαλήθευση" "Δεν ταιριάζουν" @@ -44,7 +45,7 @@ "Για επιπλέον ασφάλεια, ένας άλλος χρήστης θέλει να επαληθεύσει την ταυτότητά σας. Θα σας εμφανιστεί μια σειρά από emojis για να τα συγκρίνετε." "Πρόκειται να δεις ένα αναδυόμενο παράθυρο στην άλλη συσκευή. Ξεκίνα την επαλήθευση από εκεί τώρα." "Έναρξη επαλήθευσης στην άλλη συσκευή" - "Αναμονή για την άλλη συσκευή" + "Έναρξη επαλήθευσης στην άλλη συσκευή" "Αναμονή για τον άλλο χρήστη" "Μόλις γίνει αποδεκτό, θα μπορείτε να συνεχίσετε με την επαλήθευση." "Αποδέξου το αίτημα για να ξεκινήσεις τη διαδικασία επαλήθευσης στην άλλη συνεδρία σου για να συνεχίσεις." diff --git a/features/verifysession/impl/src/main/res/values-eo/translations.xml b/features/verifysession/impl/src/main/res/values-eo/translations.xml index d4b40184a1..71beec704e 100644 --- a/features/verifysession/impl/src/main/res/values-eo/translations.xml +++ b/features/verifysession/impl/src/main/res/values-eo/translations.xml @@ -5,8 +5,12 @@ "Confirm it\'s you" "Use backup password" "Device confirmed" + "Confirm that the emojis below match those shown on your other session." + "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted." "Now you can trust this user when sending or receiving messages." + "Device confirmed" "Enter backup password" + "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted." "Device confirmed" "Open the app on another confirmed device" "For extra security, another user wants to verify you. You\'ll be shown a set of emojis to compare." diff --git a/features/verifysession/impl/src/main/res/values-es/translations.xml b/features/verifysession/impl/src/main/res/values-es/translations.xml index 0f92e96395..19d471cb2b 100644 --- a/features/verifysession/impl/src/main/res/values-es/translations.xml +++ b/features/verifysession/impl/src/main/res/values-es/translations.xml @@ -18,6 +18,7 @@ "Comparar números" "Tu nueva sesión ya está verificada. Tienes acceso a tus mensajes cifrados y otros usuarios lo considerarán de confianza." "Ahora puedes confiar en la identidad de este usuario al enviar o recibir mensajes." + "Dispositivo verificado" "Introduce la clave de recuperación" "O bien se agotó el tiempo de solicitud, se rechazó la solicitud o hubo una discrepancia en la verificación." "Demuestra que eres tú para acceder a tu historial de mensajes cifrados." @@ -32,7 +33,7 @@ "Verificación fallida" "Continúa solo si has iniciado esta verificación." "Verifica el otro dispositivo para mantener seguro tu historial de mensajes." - "Ahora puedes leer o enviar mensajes de forma segura en tu otro dispositivo." + "Tu nueva sesión ya está verificada. Tienes acceso a tus mensajes cifrados y otros usuarios lo considerarán de confianza." "Dispositivo verificado" "Verificación solicitada" "No coinciden" @@ -44,7 +45,7 @@ "Para mayor seguridad, otro usuario quiere verificar tu identidad. Se te mostrará un conjunto de emojis para compararlos." "Deberías ver una ventana emergente en el otro dispositivo. Inicia ahora la verificación desde allí." "Iniciar la verificación en el otro dispositivo" - "A la espera del otro dispositivo" + "Iniciar la verificación en el otro dispositivo" "A la espera del otro usuario" "Una vez aceptada, podrás continuar con la verificación." "Acepta la solicitud para iniciar el proceso de verificación en tu otra sesión para continuar." diff --git a/features/verifysession/impl/src/main/res/values-et/translations.xml b/features/verifysession/impl/src/main/res/values-et/translations.xml index fa2a5733da..2c43794b72 100644 --- a/features/verifysession/impl/src/main/res/values-et/translations.xml +++ b/features/verifysession/impl/src/main/res/values-et/translations.xml @@ -16,8 +16,9 @@ "Palun kinnita, et allpool näidatud emojid vastavad täpselt neile, mida kuvatakse teise kasutaja seadmes." "Kinnita, et kõik järgnevalt kuvatud numbrid on täpselt samad, mida sa näed oma teises sessioonis." "Võrdle numbreid" - "Sinu uus sessioon on nüüd verifitseeritud. Sellel sessioonil on nüüd ligipääs sinu krüptitud sõnumitele ja teised osapooled näevad teda usaldusväärsena." + "Võid nüüd sõnumeid oma teises seadmes turvaliselt saata ja vastu võtta." "Nüüd sa võid sõnumite vastuvõtmisel ja saatmisel selle kasutaja identiteeti usaldada." + "Seade on verifitseeritud" "Sisesta taastevõti" "Kas verifitseerimine aegus, teine osapool keeldus vastamast või tekkis vastuste mittevastavus." "Saamaks ligipääsu krüptitud sõnumite ajaloole tõesta et tegemist on sinuga." @@ -44,7 +45,7 @@ "Lisaturvalisuse nimel soovib teine kasutaja sinu identiteeti verifitseerida. Järgmiseks näed sa emojisid, mida peate omavahel võrdlema." "Sa peaksid teises seadmes nägema hüpikakent. Palun alusta sealt verifitseerimist." "Alusta verifitseerimist teises seadmes" - "Ootame teise seadme järgi" + "Alusta verifitseerimist teises seadmes" "Ootame teise kasutaja järgi" "Kui oled nõustunud, siis saad sa verifitseerimist jätkata." "Jätkamaks nõustu verifitseerimisprotsessi alustamisega oma teises sessioonis." diff --git a/features/verifysession/impl/src/main/res/values-eu/translations.xml b/features/verifysession/impl/src/main/res/values-eu/translations.xml index 2497e73069..c6c821a0b4 100644 --- a/features/verifysession/impl/src/main/res/values-eu/translations.xml +++ b/features/verifysession/impl/src/main/res/values-eu/translations.xml @@ -15,6 +15,7 @@ "Egiaztatu beheko zenbakiak zure beste saioan erakutsitakoekin bat datozela." "Konparatu zenbakiak" "Saio berria egiaztatu da. Zifratutako mezu guztiak atzitu ditzake, eta gainerako erabiltzaileek fidagarritzat izango dute." + "Gailua egiaztatu da" "Sartu berreskuratze-gakoa" "Frogatu zeu zarela zifratutako mezuen historia atzitzeko." "Ireki lehendik hasita dagoen saio bat" @@ -26,7 +27,7 @@ "Egiaztapenak huts egin du" "Egiaztapen hau zeuk hasi baduzu bakarrik jarraitu." "Egiaztatu beste gailua zure mezuen historia seguru mantentzeko." - "Orain mezuak modu seguruan irakurri edo bidal ditzakezu beste gailuan." + "Saio berria egiaztatu da. Zifratutako mezu guztiak atzitu ditzake, eta gainerako erabiltzaileek fidagarritzat izango dute." "Gailua egiaztatu da" "Egiaztapena eskatu da" "Ez datoz bat" @@ -38,7 +39,7 @@ "Segurtasun handiagorako, beste erabiltzaile batek zure identitatea egiaztatu nahi du. Emoji sorta bat erakutsiko zaizu konparatzeko." "Beste gailuan laster-menu bat ikusi beharko zenuke. Hasi egiaztapena hortik orain." "Hasi egiaztapena beste gailuan" - "Beste gailuaren zain" + "Hasi egiaztapena beste gailuan" "Beste erabiltzailearen zain" "Onartutakoan egiaztapenarekin jarraitu ahal izango duzu." "Jarraitzeko, onartu zure beste saioan egiaztapen-prozesua hasteko eskaera." diff --git a/features/verifysession/impl/src/main/res/values-fa/translations.xml b/features/verifysession/impl/src/main/res/values-fa/translations.xml index 4395789a51..ace791d1b0 100644 --- a/features/verifysession/impl/src/main/res/values-fa/translations.xml +++ b/features/verifysession/impl/src/main/res/values-fa/translations.xml @@ -15,6 +15,7 @@ "مقایسهٔ شکلک‌ها" "مقایسهٔ اعداد" "اکنون نشست جدیدتان تأیید شده‌. این نشست به پیام‌های رمزنگارش شده‌تان دسترسی داشته و دیگر کاربران مطمئن می‌بینندش." + "افزاره تأیید شده" "ورود کلید بازیابی" "برای دسترسی به تاریخچه پیام‌های رمزگذاری‌شده‌تان، ثابت کنید که خودتان هستید." "گشودن نشستی موجود" @@ -24,6 +25,7 @@ "مقایسهٔ مجموعه‌ای یکتا از شکلک‌ها." "شکلک‌ها را مقایسه کنید، از ترتیب نمایش آنان نیز مطمئن شوید." "صحت‌سنجی شکست خورد" + "اکنون نشست جدیدتان تأیید شده‌. این نشست به پیام‌های رمزنگارش شده‌تان دسترسی داشته و دیگر کاربران مطمئن می‌بینندش." "افزاره تأیید شده" "مطابق نیستند" "مطابقند" diff --git a/features/verifysession/impl/src/main/res/values-fi/translations.xml b/features/verifysession/impl/src/main/res/values-fi/translations.xml index 49e09aab99..4ee897ba92 100644 --- a/features/verifysession/impl/src/main/res/values-fi/translations.xml +++ b/features/verifysession/impl/src/main/res/values-fi/translations.xml @@ -16,8 +16,9 @@ "Vahvista, että alla olevat hymiöt vastaavat toisen käyttäjän laitteessa näkyviä hymiöitä." "Varmista, että alla olevat numerot vastaavat toisessa istunnossa näkyviä numeroita." "Vertaa numeroita" - "Uusi kirjautumisesi on nyt vahvistettu. Sillä on pääsy salattuihin viesteihisi, ja muut käyttäjät näkevät sen luotettuna." + "Nyt voit lukea tai lähettää viestejä turvallisesti toisella laitteellasi." "Nyt voit luottaa tämän käyttäjän identiteettiin, kun lähetät tai vastaanotat viestejä." + "Laite vahvistettu" "Syötä palautusavain" "Joko pyyntö aikakatkaistiin, pyyntö hylättiin tai vahvistus ei täsmännyt." "Vahvista, että se olet sinä, jotta näet aiemmat salatut viestisi." @@ -44,7 +45,7 @@ "Toinen käyttäjä haluaa vahvistaa identiteettisi turvallisuuden lisäämiseksi. Sinulle näytetään joukko emojeja vertailtavaksi." "Sinun pitäisi nähdä ponnahdusikkuna toisessa laitteessa. Aloita vahvistus nyt sieltä." "Aloita vahvistus toisella laitteella" - "Odotetaan toista laitetta" + "Aloita vahvistus toisella laitteella" "Odotetaan toista käyttäjää" "Kun pyyntö on hyväksytty, voit jatkaa vahvistusta." "Hyväksy vahvistuspyyntö toisella laitteella jatkaaksesi." diff --git a/features/verifysession/impl/src/main/res/values-fr/translations.xml b/features/verifysession/impl/src/main/res/values-fr/translations.xml index a675c879f2..7417a10bfa 100644 --- a/features/verifysession/impl/src/main/res/values-fr/translations.xml +++ b/features/verifysession/impl/src/main/res/values-fr/translations.xml @@ -11,13 +11,14 @@ "Utiliser une autre session" "En attente d’une autre session…" "Quelque chose ne va pas. Soit la demande a expiré, soit elle a été refusée." - "Confirmez que les émojis ci-dessous correspondent à ceux affichés sur votre autre session." + "Confirmez que les émojis ci-dessous correspondent à ceux affichés sur votre autre appareil." "Comparez les émojis" "Vérifiez que les émojis ci-dessous correspondent à ceux affichés sur l’appareil de l’autre utilisateur." "Confirmez que les nombres ci-dessous correspondent à ceux affichés sur votre autre session." "Comparez les nombres" - "Votre nouvelle session est désormais vérifiée. Elle a accès à vos messages chiffrés et les autres utilisateurs la verront identifiée comme fiable." + "Vous pouvez désormais lire ou envoyer des messages en toute sécurité sur votre autre appareil." "Vous pouvez désormais avoir confiance en l’identité de cet utilisateur lorsque vous lui envoyez des messages ou que vous recevez des messages de sa part." + "Session vérifiée" "Utiliser la clé de récupération" "Soit la demande a expiré, soit elle a été refusée, soit les éléments à comparer ne correspondaient pas." "Prouvez qu’il s’agit bien de vous pour accéder à l’historique de vos messages chiffrés." @@ -44,7 +45,7 @@ "Pour plus de sécurité, cet autre utilisateur souhaite vérifier votre identité. Des émojis à comparer vous seront présentés." "Vous devriez voir une alerte sur l’autre appareil. Démarrez la vérification à partir de là dès maintenant." "Démarrer la vérification sur l’autre appareil" - "En attente de l’autre appareil" + "Démarrer la vérification sur l’autre appareil" "En attente de l’autre utilisateur" "Une fois acceptée, vous pourrez poursuivre la vérification." "Pour continuer, acceptez la demande de lancement de la procédure de vérification dans votre autre session." diff --git a/features/verifysession/impl/src/main/res/values-hu/translations.xml b/features/verifysession/impl/src/main/res/values-hu/translations.xml index 56abe18d67..a86a22fd1d 100644 --- a/features/verifysession/impl/src/main/res/values-hu/translations.xml +++ b/features/verifysession/impl/src/main/res/values-hu/translations.xml @@ -11,13 +11,14 @@ "Másik eszköz használata" "Várakozás a másik eszközre…" "Valami hibásnak tűnik. A kérés vagy időtúllépésre futott, vagy elutasították." - "Erősítse meg, hogy a lenti emodzsik egyeznek-e a másik munkamenetben megjelenítettekkel." + "Erősítse meg, hogy a lenti emodzsik megegyeznek a másik eszközön megjelenítettekkel." "Emodzsik összehasonlítása" "Ellenőrizze, hogy az alábbi emodzsik megegyeznek-e a másik felhasználó eszközén látható emodzsikkal." "Ellenőrizze, hogy az alábbi számok megegyeznek-e a másik munkamenetben feltüntetett számokkal." "Számok összehasonlítása" - "Az új munkamenete most már ellenőrizve van. Eléri a titkosított üzeneteit, és a többi felhasználó is megbízhatónak fogja látni." + "Mostantól biztonságosan olvashat vagy küldhet üzeneteket a másik eszközén." "Mostantól megbízhat a felhasználó személyazonosságában, amikor üzeneteket küld vagy fogad." + "Eszköz ellenőrizve" "Adja meg a helyreállítási kulcsot" "A kérés túllépte az időkorlátot, el lett utasítva, vagy ellenőrzési eltérés történt." "Bizonyítsa, hogy valóban Ön az, hogy elérje a titkosított üzeneteinek előzményeit." @@ -44,7 +45,7 @@ "A további biztonság érdekében egy másik felhasználó ellenőrizni szeretné személyazonosságát. Meg fog jelenni egy sor emodzsi, melyeket össze kell majd hasonlítania." "A másik eszközön egy felugró ablaknak kell megjelennie. Kezdje el az ellenőrzést onnan." "Ellenőrzés megkezdése a másik eszközön" - "Várakozás a másik eszközre" + "Ellenőrzés megkezdése a másik eszközön" "Várakozás a másik felhasználóra" "Az elfogadása után folytathatja az ellenőrzést." "A folytatáshoz fogadja el az ellenőrzési folyamat indítási kérését a másik munkamenetében." diff --git a/features/verifysession/impl/src/main/res/values-in/translations.xml b/features/verifysession/impl/src/main/res/values-in/translations.xml index 655520e51f..e53c3339e5 100644 --- a/features/verifysession/impl/src/main/res/values-in/translations.xml +++ b/features/verifysession/impl/src/main/res/values-in/translations.xml @@ -18,6 +18,7 @@ "Bandingkan angka" "Sesi baru Anda sekarang diverifikasi. Ini memiliki akses ke pesan terenkripsi Anda, dan pengguna lain akan melihatnya sebagai tepercaya." "Sekarang Anda dapat mempercayai identitas pengguna ini saat mengirim atau menerima pesan." + "Perangkat terverifikasi" "Masukkan kunci pemulihan" "Entah permintaan habis waktu, permintaan ditolak, atau ada ketidakcocokan verifikasi." "Buktikan bahwa ini memang Anda untuk mengakses riwayat pesan terenkripsi Anda." @@ -32,7 +33,7 @@ "Verifikasi gagal" "Lanjutkan hanya jika Anda memulai verifikasi ini." "Verifikasi perangkat lain untuk menjaga riwayat pesan Anda tetap aman." - "Sekarang Anda dapat membaca atau mengirim pesan dengan aman di perangkat Anda yang lain." + "Sesi baru Anda sekarang diverifikasi. Ini memiliki akses ke pesan terenkripsi Anda, dan pengguna lain akan melihatnya sebagai tepercaya." "Perangkat terverifikasi" "Verifikasi diminta" "Mereka tidak cocok" @@ -44,7 +45,7 @@ "Untuk keamanan tambahan, pengguna lain ingin memverifikasi identitas Anda. Anda akan ditampilkan satu set emoji untuk dibandingkan." "Anda akan melihat popup di perangkat lain. Mulai verifikasi dari sana sekarang." "Mulai verifikasi di perangkat lain" - "Menunggu perangkat lain" + "Mulai verifikasi di perangkat lain" "Menunggu pengguna lain" "Setelah diterima, Anda akan dapat melanjutkan verifikasi." "Terima permintaan untuk memulai proses verifikasi di sesi Anda yang lain untuk melanjutkan." diff --git a/features/verifysession/impl/src/main/res/values-it/translations.xml b/features/verifysession/impl/src/main/res/values-it/translations.xml index bb55310d31..3be984865e 100644 --- a/features/verifysession/impl/src/main/res/values-it/translations.xml +++ b/features/verifysession/impl/src/main/res/values-it/translations.xml @@ -18,6 +18,7 @@ "Confronta i numeri" "La tua nuova sessione è ora verificata. Ha accesso ai tuoi messaggi crittografati e gli altri utenti la vedranno come attendibile." "Ora puoi fidarti dell\'identità di questo utente quando invii o ricevi messaggi." + "Dispositivo verificato" "Inserisci la chiave di recupero" "La richiesta è scaduta, è stata rifiutata o c\'è stata una mancata corrispondenza nella verifica." "Dimostra la tua identità per accedere alla cronologia dei messaggi crittografati." @@ -32,7 +33,7 @@ "Verifica fallita" "Continua solo se tu hai avviato questa verifica." "Verifica l\'altro dispositivo per proteggere la cronologia dei messaggi." - "Ora puoi leggere o inviare messaggi in modo sicuro sull\'altro dispositivo." + "La tua nuova sessione è ora verificata. Ha accesso ai tuoi messaggi crittografati e gli altri utenti la vedranno come attendibile." "Dispositivo verificato" "Richiesta di verifica" "Non corrispondono" @@ -44,7 +45,7 @@ "Per una maggiore sicurezza, un altro utente desidera verificare la tua identità. Ti verrà mostrato un set di emoji da confrontare." "Dovresti vedere un popup sull\'altro dispositivo. Inizia subito la verifica da lì." "Avvia la verifica sull\'altro dispositivo" - "In attesa dell\'altro dispositivo" + "Avvia la verifica sull\'altro dispositivo" "In attesa dell\'altro utente" "Una volta accettata, potrai proseguire con la verifica." "Accetta la richiesta di avviare il processo di verifica nell\'altra sessione per continuare." diff --git a/features/verifysession/impl/src/main/res/values-ka/translations.xml b/features/verifysession/impl/src/main/res/values-ka/translations.xml index 5815ecefaa..eed71a3b61 100644 --- a/features/verifysession/impl/src/main/res/values-ka/translations.xml +++ b/features/verifysession/impl/src/main/res/values-ka/translations.xml @@ -12,6 +12,7 @@ "დაადასტურეთ, რომ ქვემოთ მოცემული ნომრები ემთხვევა თქვენს სხვა სესიაზე ნაჩვენები ნომრებს." "შეადარეთ რიცხვები" "თქვენი ახალი სესია დადასტურებულია. მას აქვს წვდომა დაშიფრულ შეტყობინებებზე და სხვა მომხმარებლები მას სანდოდ ხედავენ." + "მოწყობილობა დადასტურებულია" "შეიყვანეთ აღდგენის გასაღები" "დაამტკიცეთ, რომ ეს თქვენ ხართ, რათა მიიღოთ წვდომა თქვენი დაშიფრული შეტყობინებების ისტორიასთან." "არსებული სესიის გახსნა" @@ -20,6 +21,7 @@ "ველოდებით დამთხვევას" "შეადარეთ ემოციების უნიკალური ნაკრები." "შეადარეთ უნიკალური ემოჯი, დარწმუნდით, რომ ისინი ერთი დ იმავე თანმიმდევრობით გამოჩნდნენ." + "თქვენი ახალი სესია დადასტურებულია. მას აქვს წვდომა დაშიფრულ შეტყობინებებზე და სხვა მომხმარებლები მას სანდოდ ხედავენ." "მოწყობილობა დადასტურებულია" "ისინი არ ემთხვევიან ერთმანეთს" "ისინი ემთხვევიან ერთმანეთს" diff --git a/features/verifysession/impl/src/main/res/values-ko/translations.xml b/features/verifysession/impl/src/main/res/values-ko/translations.xml index f04ba0ba08..1dfe3f752a 100644 --- a/features/verifysession/impl/src/main/res/values-ko/translations.xml +++ b/features/verifysession/impl/src/main/res/values-ko/translations.xml @@ -18,6 +18,7 @@ "숫자 비교" "새로운 세션이 확인되었습니다. 이 세션은 귀하의 암호화된 메시지에 액세스할 수 있으며, 다른 사용자는 이 세션을 신뢰할 수 있는 세션으로 인식합니다." "이제 메시지를 보내거나 받을 때 이 사용자의 신원을 신뢰할 수 있습니다." + "기기 검증됨" "복구 키를 입력하세요" "요청이 시간 초과되었거나, 요청이 거부되었거나, 검증 불일치가 발생했습니다." "암호화된 메시지 기록에 액세스하기 위해 본인임을 증명하세요." @@ -32,7 +33,7 @@ "검증 실패" "본인이 이 검증을 시작한 경우에만 계속 진행하세요." "다른 기기를 확인하여 메시지 기록을 안전하게 보호하세요." - "이제 다른 기기에서도 안전하게 메시지를 읽거나 보낼 수 있습니다." + "새로운 세션이 확인되었습니다. 이 세션은 귀하의 암호화된 메시지에 액세스할 수 있으며, 다른 사용자는 이 세션을 신뢰할 수 있는 세션으로 인식합니다." "기기 검증됨" "검증 요청" "일치하지 않습니다" @@ -44,7 +45,7 @@ "추가 보안 위해 다른 사용자가 귀하의 신원을 확인하고자 합니다. 비교할 이모티콘 세트가 표시됩니다." "다른 기기에 팝업이 표시될 것입니다. 지금 그곳에서 확인을 시작하세요." "다른 장치에서 검증 시작" - "다른 기기를 기다리고 있습니다" + "다른 장치에서 검증 시작" "다른 사용자를 기다리는 중" "승인 후에는 검증 과정을 계속 진행할 수 있습니다." "계속하려면 다른 세션에서 검증 과정을 시작하라는 요청을 수락하세요." diff --git a/features/verifysession/impl/src/main/res/values-lt/translations.xml b/features/verifysession/impl/src/main/res/values-lt/translations.xml index 0fd7a2e755..34ea11d141 100644 --- a/features/verifysession/impl/src/main/res/values-lt/translations.xml +++ b/features/verifysession/impl/src/main/res/values-lt/translations.xml @@ -10,6 +10,7 @@ "Aš pasiruošęs" "Laukiama atitikimo…" "Palyginkite unikalius jaustukus, įsitikindami, kad jie rodomi ta pačia tvarka." + "Jūsų nauja sesija dabar patvirtinta. Ji turi prieigą prie jūsų užšifruotų pranešimų, o kiti vartotojai matys ją kaip patikimą." "Jie nesutampa" "Jie sutampa" "Kitoje sesijoje priimkite prašymą pradėti tikrinimo procesą, kad galėtumėte tęsti." diff --git a/features/verifysession/impl/src/main/res/values-nb/translations.xml b/features/verifysession/impl/src/main/res/values-nb/translations.xml index afb8298a20..533a0fde1d 100644 --- a/features/verifysession/impl/src/main/res/values-nb/translations.xml +++ b/features/verifysession/impl/src/main/res/values-nb/translations.xml @@ -11,13 +11,14 @@ "Bruk en annen enhet" "Venter på en annen enhet…" "Noe virker ikke riktig. Enten ble forespørselen tidsavbrutt eller forespørselen ble avslått." - "Bekreft at emojiene nedenfor samsvarer med de som vises på den andre sesjonen din." + "Bekreft at emojiene nedenfor samsvarer med de som vises på den andre enheten din." "Sammenlign emojier" "Bekreft at emojiene nedenfor stemmer overens med de som vises på den andre brukerens enhet." "Kontroller at tallene nedenfor stemmer overens med dem som vises på den andre sesjonen." "Sammenlign tallene" - "Den nye sesjonen din er nå bekreftet. Den har tilgang til de krypterte meldingene dine, og andre brukere vil se den som klarert." + "Nå kan du lese eller sende meldinger sikkert på den andre enheten din." "Nå kan du stole på identiteten til denne brukeren når du sender eller mottar meldinger." + "Enhet verifisert" "Skriv inn gjenopprettingsnøkkel" "Enten ble forespørselen tidsavbrutt, forespørselen ble avslått eller det var en feil i verifiseringen." "Bevis at det er deg for å få tilgang til den krypterte meldingshistorikken din." @@ -44,7 +45,7 @@ "For ekstra sikkerhet vil en annen bruker bekrefte identiteten din. Du får se et sett med emojier som du må sammenligne." "Du skal se en popup på den andre enheten. Start bekreftelsen derfra nå." "Start verifiseringen på den andre enheten" - "Venter på den andre enheten" + "Start verifiseringen på den andre enheten" "Venter på den andre brukeren" "Når du er akseptert, kan du fortsette med verifiseringen." "Godta forespørselen om å starte bekreftelsesprosessen i den andre sesjonen for å fortsette." diff --git a/features/verifysession/impl/src/main/res/values-nl/translations.xml b/features/verifysession/impl/src/main/res/values-nl/translations.xml index 54797b1244..d7bc1d4172 100644 --- a/features/verifysession/impl/src/main/res/values-nl/translations.xml +++ b/features/verifysession/impl/src/main/res/values-nl/translations.xml @@ -16,6 +16,7 @@ "Bevestig dat de onderstaande cijfers overeenkomen met de cijfers die worden weergegeven in je andere sessie." "Vergelijk getallen" "Je nieuwe sessie is nu geverifieerd. Het heeft toegang tot je versleutelde berichten en andere gebruikers zullen het als vertrouwd beschouwen." + "Apparaat geverifieerd" "Voer herstelsleutel in" "Bewijs dat jij het bent om toegang te krijgen tot je versleutelde berichtgeschiedenis." "Open een bestaande sessie" @@ -27,6 +28,7 @@ "Ingelogd" "Ga alleen verder als je deze verificatie hebt gestart." "Verifieer het andere apparaat om je berichtengeschiedenis veilig te houden." + "Je nieuwe sessie is nu geverifieerd. Het heeft toegang tot je versleutelde berichten en andere gebruikers zullen het als vertrouwd beschouwen." "Apparaat geverifieerd" "Verificatieverzocht" "Ze komen niet overeen" diff --git a/features/verifysession/impl/src/main/res/values-pl/translations.xml b/features/verifysession/impl/src/main/res/values-pl/translations.xml index d5cd461b8d..f76f2c31fa 100644 --- a/features/verifysession/impl/src/main/res/values-pl/translations.xml +++ b/features/verifysession/impl/src/main/res/values-pl/translations.xml @@ -18,6 +18,7 @@ "Porównaj liczby" "Twoja nowa sesja jest teraz zweryfikowana. Ma ona dostęp do Twoich zaszyfrowanych wiadomości, a inni użytkownicy będą widzieć ją jako zaufaną." "Teraz możesz zaufać tożsamości tego użytkownika podczas wysyłania lub odbierania wiadomości." + "Urządzenie zweryfikowane" "Wprowadź klucz przywracania" "Albo upłynął limit czasu żądania, albo żądanie zostało odrzucone, albo wystąpił błąd weryfikacji." "Udowodnij, że to ty, aby uzyskać dostęp do historii zaszyfrowanych wiadomości." @@ -32,7 +33,7 @@ "Weryfikacja nie powiodła się" "Kontynuuj tylko, jeśli to Ty zainicjowałeś tę weryfikację." "Zweryfikuj drugie urządzenie, aby zabezpieczyć historię wiadomości." - "Już możesz bezpiecznie czytać lub wysyłać wiadomości na drugim urządzeniu." + "Twoja nowa sesja jest teraz zweryfikowana. Ma ona dostęp do Twoich zaszyfrowanych wiadomości, a inni użytkownicy będą widzieć ją jako zaufaną." "Urządzenie zweryfikowane" "Zażądano weryfikacji" "Nie pasują do siebie" @@ -44,7 +45,7 @@ "Dla dodatkowej ochrony, inny użytkownik chce zweryfikować Twoją tożsamość. Pojawi się unikalny zestaw emoji do porównania." "Powinno wyskoczyć okno na drugim urządzeniu. Rozpocznij tam weryfikację." "Rozpocznij weryfikację na drugim urządzeniu" - "Oczekiwanie na drugie urządzenie" + "Rozpocznij weryfikację na drugim urządzeniu" "Oczekiwanie na drugiego użytkownika" "Po zaakceptowaniu będziesz mógł kontynuować weryfikację." "Zaakceptuj prośbę o rozpoczęcie procesu weryfikacji w innej sesji, aby kontynuować." diff --git a/features/verifysession/impl/src/main/res/values-pt-rBR/translations.xml b/features/verifysession/impl/src/main/res/values-pt-rBR/translations.xml index ad3b15869a..8dd7c9a2f7 100644 --- a/features/verifysession/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/verifysession/impl/src/main/res/values-pt-rBR/translations.xml @@ -18,6 +18,7 @@ "Comparar números" "Sua nova sessão está verificada agora. Ela tem acesso às suas mensagens criptografadas e outros usuários a verão como confiável." "Agora você pode confiar na identidade desse usuário ao enviar ou receber mensagens." + "Dispositivo verificado" "Digitar chave de recuperação" "Ou a solicitação expirou, a solicitação foi negada ou houve uma não correspondência na verificação." "Prove que é você para acessar seu histórico de mensagens criptografadas." @@ -32,7 +33,7 @@ "A verificação falhou" "Continue somente se você iniciou esta verificação." "Verifique o outro dispositivo para manter seu histórico de mensagens seguro." - "Agora você pode ler ou enviar mensagens com segurança em seu outro dispositivo." + "Sua nova sessão está verificada agora. Ela tem acesso às suas mensagens criptografadas e outros usuários a verão como confiável." "Dispositivo verificado" "Verificação solicitada" "Eles não combinam" @@ -44,7 +45,7 @@ "Para maior segurança, outro usuário deseja verificar sua identidade. Você verá um conjunto de emojis para comparar." "Você deverá ver um pop-up no outro dispositivo. Você deverá ver uma janela pop-up no outro dispositivo e iniciar a verificação a partir daí." "Inicie a verificação no outro dispositivo" - "Aguardando o outro dispositivo" + "Inicie a verificação no outro dispositivo" "Aguardando o outro usuário" "Depois de aceito, você poderá continuar com a verificação." "Aceite a solicitação para iniciar o processo de verificação na sua outra sessão para continuar." diff --git a/features/verifysession/impl/src/main/res/values-pt/translations.xml b/features/verifysession/impl/src/main/res/values-pt/translations.xml index ce2eb2af15..bf32d3c7b9 100644 --- a/features/verifysession/impl/src/main/res/values-pt/translations.xml +++ b/features/verifysession/impl/src/main/res/values-pt/translations.xml @@ -11,13 +11,14 @@ "Utilizar outro dispositivo" "A aguardar por outros dispositivos…" "Algo não bateu certo. O pedido ou demorou demasiado tempo ou foi rejeitado." - "Confirma se os emojis abaixo correspondem aos apresentados na tua outra sessão." + "Confirma que os emojis abaixo correspondem aos apresentados no teu outro dispositivo." "Compara os emojis" "Confirma se os emojis abaixo correspondem aos apresentados no dispositivo do outro utilizador." "Confirma se os números abaixo correspondem aos números apresentados na tua outra sessão." "Comparar números" - "A tua nova sessão está agora verificada, pelo que tem acesso às tuas mensagens cifradas e os outros utilizadores vão vê-la como de confiança." + "Agora já podes ler ou enviar mensagens com segurança a partir do teu outro dispositivo." "Agora podes confiar na identidade deste utilizador quando envias ou recebes mensagens." + "Dispositivo verificado" "Insere a chave de recuperação" "O pedido expirou, o pedido foi recusado ou houve um erro de verificação." "Prova que és tu para acederes ao teu histórico de mensagens cifradas." @@ -32,7 +33,7 @@ "A verificação falhou" "Continua apenas se tiveres iniciado esta verificação." "Verifique o outro dispositivo para manter o histórico de mensagens seguro." - "Agora podes ler ou enviar mensagens de forma segura no teu outro dispositivo." + "Agora já podes ler ou enviar mensagens com segurança a partir do teu outro dispositivo." "Dispositivo verificado" "Verificação solicitada" "Não correspondem" @@ -44,7 +45,7 @@ "Para maior segurança, outro utilizador quer verificar a tua identidade. Ser-te-á mostrado um conjunto de emojis para comparares." "Deves ver uma notificação no outro dispositivo. Inicia a verificação a partir daí." "Inicia a verificação no outro dispositivo" - "À espera do outro dispositivo" + "Inicia a verificação no outro dispositivo" "A espera do outro utilizador" "Uma vez aceite, poderás continuar com a verificação." "Para continuar, aceita o pedido de verificação na tua outra sessão." diff --git a/features/verifysession/impl/src/main/res/values-ro/translations.xml b/features/verifysession/impl/src/main/res/values-ro/translations.xml index 08064b056a..0d1ddd0530 100644 --- a/features/verifysession/impl/src/main/res/values-ro/translations.xml +++ b/features/verifysession/impl/src/main/res/values-ro/translations.xml @@ -16,8 +16,9 @@ "Confirmați că emoji-urile de mai jos corespund cu cele afișate pe dispozitivul celuilalt utilizator." "Confirmați că numerele de mai jos se potrivesc cu cele afișate în cealaltă sesiune." "Comparați numerele" - "Noua dumneavoastră sesiune este acum verificată. Are acces la mesajele dumneavoastră criptate, iar alți utilizatori vă vor vedea ca fiind de încredere." + "Noua dumneavoastră sesiune este acum verificată. Are acces la mesajele dumneavoastră criptate, iar ceilalti utilizatori vă vor vedea ca fiind de încredere." "Acum puteți avea încredere în identitatea acestui utilizator atunci când trimiteți sau primiți mesaje." + "Dispozitiv verificat" "Introduceți cheia de recuperare" "Fie cererea a expirat, cererea a fost respinsă, fie a existat o nepotrivire de verificare." "Demonstrați-vă identitatea pentru a accesa mesaje anterioare criptate." @@ -32,7 +33,7 @@ "Verificarea a eșuat" "Continuați numai dacă dumneavoastră ați inițiat această verificare." "Verificați celălalt dispozitiv pentru a vă păstra mesajele anterioare în siguranță." - "Acum puteți citi sau trimite mesaje în siguranță pe celălalt dispozitiv." + "Noua dumneavoastră sesiune este acum verificată. Are acces la mesajele dumneavoastră criptate, iar ceilalti utilizatori vă vor vedea ca fiind de încredere." "Dispozitiv verificat" "Verificare cerută" "Nu se potrivesc" @@ -44,7 +45,7 @@ "Pentru o securitate suplimentară, un alt utilizator dorește să vă verifice identitatea. Vi se va afișa un set de emoji-uri pentru comparație." "Ar trebui să vedeți o fereastră pop-up pe celălalt dispozitiv. Începeți verificarea de acolo acum." "Începeți verificarea pe celălalt dispozitiv" - "Se așteaptă celălalt dispozitiv" + "Începeți verificarea pe celălalt dispozitiv" "Se așteaptă celălalt utilizator" "După acceptare, veți putea continua verificarea." "Acceptați cererea de a începe procesul de verificare în cealaltă sesiune pentru a continua." diff --git a/features/verifysession/impl/src/main/res/values-ru/translations.xml b/features/verifysession/impl/src/main/res/values-ru/translations.xml index ace2354837..0c2347a2ea 100644 --- a/features/verifysession/impl/src/main/res/values-ru/translations.xml +++ b/features/verifysession/impl/src/main/res/values-ru/translations.xml @@ -11,13 +11,14 @@ "Использовать другое устройство" "Ожидание на другом устройстве…" "Похоже, что-то не так. Время ожидания запроса либо истекло, либо запрос был отклонен." - "Убедитесь, что приведенные ниже емоджи совпадают с емоджи показанными во время другого сеанса." + "Убедитесь, что приведенные ниже эмодзи совпадают с эмодзи показанными на другом устройстве." "Сравните емодзи" "Убедитесь, что указанные ниже эмодзи соответствуют тем, которые отображаются на устройстве другого пользователя." "Убедитесь, что приведенные ниже числа совпадают с цифрами, показанными в другом сеансе." "Сравните числа" - "Ваш новый сеанс подтвержден. У него есть доступ к вашим зашифрованным сообщениям, и другие пользователи увидят его как доверенное." + "Теперь вы можете читать или отправлять сообщения безопасно в другом вашем устройстве." "Теперь вы можете доверять этому пользователя при отправке или получении сообщений." + "Устройство проверено" "Введите ключ восстановления" "Время ожидания подтверждения истекло, запрос был отклонён, или при подтверждении произошло несоответствие." "Чтобы получить доступ к зашифрованной истории сообщений, докажите, что это вы." @@ -32,7 +33,7 @@ "Сбой проверки" "Продолжайте только если вы ожидали данное подтверждение." "Чтобы сохранить историю сообщений в безопасности, проверьте другое устройство." - "Теперь вы можете безопасно читать или отправлять сообщения на другом устройстве." + "Теперь вы можете читать или отправлять сообщения безопасно в другом вашем устройстве." "Устройство проверено" "Запрошено подтверждение" "Они не совпадают" @@ -44,7 +45,7 @@ "Для дополнительной безопасности другой пользователь хочет проверить вашу личность. Вам будет показан набор эмодзи для сравнения." "Вы должны увидеть всплывающее окно на другом устройстве. Начните проверку оттуда прямо сейчас." "Начать проверку на другом устройстве" - "Ожидание другого устройства" + "Начать проверку на другом устройстве" "Ожидание другого пользователя" "После одобрения вы сможете продолжить проверку." "Чтобы продолжить, примите запрос на запуск процесса подтверждения в другом сеансе." diff --git a/features/verifysession/impl/src/main/res/values-sk/translations.xml b/features/verifysession/impl/src/main/res/values-sk/translations.xml index cc5f26e85b..7c4781db77 100644 --- a/features/verifysession/impl/src/main/res/values-sk/translations.xml +++ b/features/verifysession/impl/src/main/res/values-sk/translations.xml @@ -18,6 +18,7 @@ "Porovnať čísla" "Vaša nová relácia je teraz overená. Má prístup k vašim zašifrovaným správam a ostatní používatelia ju budú vidieť ako dôveryhodnú." "Teraz môžete dôverovať identite tohto používateľa pri odosielaní alebo prijímaní správ." + "Zariadenie overené" "Zadajte kľúč na obnovenie" "Buď žiadosť vypršala, žiadosť bola zamietnutá, alebo došlo k nesúladu overovania." "Dokážte, že ste to vy, aby ste získali prístup k histórii vašich zašifrovaných správ." @@ -32,7 +33,7 @@ "Overenie zlyhalo" "Pokračujte iba vtedy, ak ste toto overenie začali." "Overte druhé zariadenie, aby bola vaša história správ zabezpečená." - "Teraz môžete bezpečne čítať alebo odosielať správy na svojom druhom zariadení." + "Vaša nová relácia je teraz overená. Má prístup k vašim zašifrovaným správam a ostatní používatelia ju budú vidieť ako dôveryhodnú." "Zariadenie overené" "Vyžadované overenie" "Nezhodujú sa" @@ -44,7 +45,7 @@ "Kvôli vyššej bezpečnosti chce druhý používateľ overiť vašu identitu. Zobrazí sa vám sada emotikonov na porovnanie." "Na druhom zariadení by sa malo zobraziť vyskakovacie okno. Začnite teraz overovanie odtiaľ." "Spustiť overovanie na druhom zariadení" - "Čaká sa na druhé zariadenie" + "Spustiť overovanie na druhom zariadení" "Čaká sa na druhého používateľa" "Po prijatí budete môcť pokračovať v overovaní." "Ak chcete pokračovať, prijmite žiadosť o spustenie procesu overenia vo vašej druhej relácii." diff --git a/features/verifysession/impl/src/main/res/values-sv/translations.xml b/features/verifysession/impl/src/main/res/values-sv/translations.xml index 0fd66a82d3..3e92ab0189 100644 --- a/features/verifysession/impl/src/main/res/values-sv/translations.xml +++ b/features/verifysession/impl/src/main/res/values-sv/translations.xml @@ -18,6 +18,7 @@ "Jämför siffror" "Din nya session är nu verifierad. Den har tillgång till dina krypterade meddelanden, och andra användare kommer att se den som betrodd." "Nu kan du lita på användarens identitet när du skickar eller tar emot meddelanden." + "Enhet verifierad" "Ange återställningsnyckel" "Antingen överskreds tidsgränsen för begäran, begäran nekades eller så fanns det ett matchningsfel för verifieringen." "Bevisa att det är du för att komma åt din krypterade meddelandehistorik." @@ -32,7 +33,7 @@ "Verifiering misslyckades" "Fortsätt bara om du initierade denna verifiering." "Verifiera den andra enheten för att hålla din meddelandehistorik säker." - "Du kan nu läsa eller skicka meddelanden säkert på din andra enhet." + "Din nya session är nu verifierad. Den har tillgång till dina krypterade meddelanden, och andra användare kommer att se den som betrodd." "Enhet verifierad" "Verifiering begärd" "De matchar inte" @@ -44,7 +45,7 @@ "För extra säkerhet vill en annan användare verifiera din identitet. Du kommer att visas en uppsättning emojier att jämföra." "Du bör se en popup på den andra enheten. Starta verifieringen därifrån nu." "Starta verifieringen på den andra enheten" - "Väntar på den andra enheten" + "Starta verifieringen på den andra enheten" "Väntar på den andra användaren" "När det har accepterats kommer du kunna fortsätta verifieringen." "Godkänn begäran om att starta verifieringsprocessen på din andra session för att fortsätta." diff --git a/features/verifysession/impl/src/main/res/values-tr/translations.xml b/features/verifysession/impl/src/main/res/values-tr/translations.xml index a045731bd4..e718ee2d46 100644 --- a/features/verifysession/impl/src/main/res/values-tr/translations.xml +++ b/features/verifysession/impl/src/main/res/values-tr/translations.xml @@ -18,6 +18,7 @@ "Sayıları karşılaştır" "Yeni oturumunuz artık doğrulandı. Şifrelenmiş mesajlarınıza erişebilir ve diğer kullanıcılar oturumu güvenilir olarak görecektir." "Artık mesaj gönderirken veya alırken bu kullanıcının kimliğine güvenebilirsiniz." + "Cihaz doğrulandı" "Kurtarma anahtarını girin" "İstek zaman aşımına uğradı, istek reddedildi veya bir doğrulama uyuşmazlığı vardı." "Şifrelenmiş mesaj geçmişinize erişmek için siz olduğunuzu kanıtlayın." @@ -32,7 +33,7 @@ "Doğrulama başarısız" "Yalnızca bu doğrulamayı siz başlattıysanız devam edin." "Mesaj geçmişinizi güvende tutmak için diğer cihazı doğrulayın." - "Artık diğer cihazınızda güvenli bir şekilde mesaj okuyabilir veya gönderebilirsiniz." + "Yeni oturumunuz artık doğrulandı. Şifrelenmiş mesajlarınıza erişebilir ve diğer kullanıcılar oturumu güvenilir olarak görecektir." "Cihaz doğrulandı" "Doğrulama talep edildi" "Eşleşmiyorlar" @@ -44,7 +45,7 @@ "Ekstra güvenlik için, başka bir kullanıcı kimliğinizi doğrulamak istiyor. Karşılaştırmanız için size bir dizi emoji gösterilecektir." "Diğer cihazda bir açılır pencere görmelisiniz. Doğrulamayı şimdi oradan başlatın." "Diğer cihazda doğrulamayı başlat" - "Diğer cihaz bekleniyor" + "Diğer cihazda doğrulamayı başlat" "Diğer kullanıcı bekleniyor" "Kabul edildikten sonra doğrulama işlemine devam edebileceksiniz." "Devam etmek için diğer oturumunuzda doğrulama işlemini başlatma isteğini kabul edin." diff --git a/features/verifysession/impl/src/main/res/values-uk/translations.xml b/features/verifysession/impl/src/main/res/values-uk/translations.xml index 10c6ae184d..e28304d138 100644 --- a/features/verifysession/impl/src/main/res/values-uk/translations.xml +++ b/features/verifysession/impl/src/main/res/values-uk/translations.xml @@ -18,6 +18,7 @@ "Порівняйте цифри" "Ваш новий сеанс підтверджено. Він матиме доступ до ваших зашифрованих повідомлень, й інші користувачі вважатимуть його надійним." "Тепер ви можете довіряти особистості цього користувача під час надсилання або отримання повідомлень." + "Пристрій перевірено" "Введіть ключ відновлення" "Або час очікування запиту минув, або запит було відхилено, або виникла розбіжність у верифікації." "Доведіть, що це ви, щоб отримати доступ до історії зашифрованих повідомлень." @@ -32,7 +33,7 @@ "Перевірка не вдалася" "Продовжуйте, лише якщо ви ініціювали цю перевірку." "Перевірте інший пристрій, щоб захистити історію повідомлень." - "Тепер ви можете безпечно читати або надсилати повідомлення на іншому пристрої." + "Ваш новий сеанс підтверджено. Він матиме доступ до ваших зашифрованих повідомлень, й інші користувачі вважатимуть його надійним." "Пристрій перевірено" "Запитано на верифікацію" "Вони не збігаються" @@ -44,7 +45,7 @@ "Для додаткової безпеки інший користувач хоче верифікувати вашу особистість. Вам буде показано набір емоджі для порівняння." "Ви повинні побачити спливаюче вікно на іншому пристрої. Почніть перевірку звідти." "Почніть перевірку на іншому пристрої" - "Очікування іншого пристрою" + "Почніть перевірку на іншому пристрої" "Очікування іншого користувача" "Після погодження ви зможете продовжити верифікацію." "Щоб продовжити, прийміть запит на початок процесу верифікації в іншому сеансі." diff --git a/features/verifysession/impl/src/main/res/values-ur/translations.xml b/features/verifysession/impl/src/main/res/values-ur/translations.xml index e6f0fcd367..0697e10c00 100644 --- a/features/verifysession/impl/src/main/res/values-ur/translations.xml +++ b/features/verifysession/impl/src/main/res/values-ur/translations.xml @@ -16,6 +16,7 @@ "تصدیق کریں کہ نیچے دیے گئے اعداد آپکے دوسرے جلسے میں دکھائے گئے اعداد سے مماثل ہیں۔" "اعداد کا موازنہ کریں" "آپ کا نیا جلسہ اب توثیق شدہ ہے۔ اسے آپ کے مرموزکردہ پیغامات تک رسائی حاصل ہے، اور دوسرے صارفین اسے بھروسہ مند کے طور پر دیکھیں گے۔" + "آلہ توثیق شدہ" "بازیابی کلید درج کریں" "اپنی مرموزکردہ پیغام کی سرگزشت تک رسائی حاصل کرنے کے لیے ثابت کریں کہ یہ آپ ہی ہیں۔" "ایک موجودہ جلسہ کھولیں" @@ -24,6 +25,7 @@ "ملانے کا انتظار" "رموز تعبیری کے منفرد مجموعہ کا موازنہ کریں۔" "منفرد ایموجی کا موازنہ کریں، یقینی بناتے ہوئے کہ وہ ایک ہی ترتیب میں دکھائی دیں۔" + "آپ کا نیا جلسہ اب توثیق شدہ ہے۔ اسے آپ کے مرموزکردہ پیغامات تک رسائی حاصل ہے، اور دوسرے صارفین اسے بھروسہ مند کے طور پر دیکھیں گے۔" "آلہ توثیق شدہ" "وہ مماثل نہیں ہیں" "وہ مماثل ہیں" diff --git a/features/verifysession/impl/src/main/res/values-uz/translations.xml b/features/verifysession/impl/src/main/res/values-uz/translations.xml index defdff1102..5204e759b3 100644 --- a/features/verifysession/impl/src/main/res/values-uz/translations.xml +++ b/features/verifysession/impl/src/main/res/values-uz/translations.xml @@ -16,6 +16,7 @@ "Quyidagi raqamlarning boshqa sessiyangizda koʻrsatilgan raqamlarga mos kelishini tasdiqlang." "Sonlarni taqqoslash" "Yangi seansingiz tasdiqlandi. U sizning shifrlangan xabarlaringizga kirish huquqiga ega va boshqa foydalanuvchilar uni ishonchli deb bilishadi." + "Qurilma tasdiqlandi" "Tiklash kalitini kiriting" "So‘rov vaqti tugab qoldi, so‘rov rad etildi yoki tekshiruv mos kelmadi." "Shifrlangan xabarlar tarixiga kirish uchun shaxsingizni tasdiqlang." @@ -30,7 +31,7 @@ "Tasdiqlanmadi" "Bu tekshiruvni boshlagan bo‘lsangizgina davom eting." "Xabarlaringiz tarixini xavfsiz saqlash uchun narigi qurilmani tasdiqlang." - "Endi xabarlarni boshqa qurilmangizda xavfsiz o‘qish yoki yuborishingiz mumkin." + "Yangi seansingiz tasdiqlandi. U sizning shifrlangan xabarlaringizga kirish huquqiga ega va boshqa foydalanuvchilar uni ishonchli deb bilishadi." "Qurilma tasdiqlandi" "Tasdiqlash talab qilindi" "Ular mos kelmaydi" diff --git a/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml b/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml index 46e26a2e70..7bba014ada 100644 --- a/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml @@ -18,6 +18,7 @@ "比較數字" "新的工作階段已完成驗證。它能夠存取您的加密訊息,而其他使用者會將它視為可信任的。" "現在,您可以在傳送或接收訊息時信任此使用者的身份。" + "裝置已驗證" "輸入復原金鑰" "請求逾時、請求被拒或是驗證不符。" "為了存取被加密的歷史訊息,您需要證明這是您本人。" @@ -32,7 +33,7 @@ "驗證失敗。" "僅當您啟動此驗證時才繼續。" "驗證其他裝置以保護您的訊息歷史紀錄安全。" - "現在,您可以在其他裝置上安全地閱讀或傳送訊息。" + "新的工作階段已完成驗證。它能夠存取您的加密訊息,而其他使用者會將它視為可信任的。" "裝置已驗證" "已請求驗證" "不一樣" @@ -44,7 +45,7 @@ "為了提昇安全性,另一個使用者希望驗證您的身份。您將會看到一組表情符號以進行比較。" "您應該會在其他裝置上看到一個彈出式視窗。立刻從那裡開始驗證。" "在其他裝置上開始驗證" - "正在等待其他裝置" + "在其他裝置上開始驗證" "正在等帶齊他使用者" "接受後,您就可以繼續進行驗證。" "準備開始驗證,請到您的其他工作階段接受請求。" diff --git a/features/verifysession/impl/src/main/res/values-zh/translations.xml b/features/verifysession/impl/src/main/res/values-zh/translations.xml index 113cda4a01..92641d1773 100644 --- a/features/verifysession/impl/src/main/res/values-zh/translations.xml +++ b/features/verifysession/impl/src/main/res/values-zh/translations.xml @@ -18,6 +18,7 @@ "比较数字" "新设备已经成功验证。现在新设备可以访问加密信息,其他用户也会信任这个设备。" "现在您可以在发送或接收消息时信任该用户的身份。" + "设备已验证" "输入恢复密钥" "要么请求超时,要么请求被拒绝,要么验证不匹配。" "证明自己的身份以访问加密历史消息。" @@ -32,7 +33,7 @@ "验证失败" "仅在你发起此验证后才继续。" "验证另一台设备以确保您的消息历史记录保密。" - "现在,您可以在其他设备上安全地阅读或发送消息。" + "新设备已经成功验证。现在新设备可以访问加密信息,其他用户也会信任这个设备。" "设备已验证" "已请求验证" "不匹配" @@ -44,7 +45,7 @@ "为了提高安全性,另一位用户想要验证您的身份。您将看到一组表情符号供您比较。" "您应该会在另一台设备上看到一个弹出窗口。现在从那里开始验证。" "在另一台设备上开始验证" - "正在等待其他设备" + "在另一台设备上开始验证" "等待其他用户" "一旦被接受,您将能够继续进行验证。" "请在其他会话中接受验证请求。" diff --git a/features/verifysession/impl/src/main/res/values/localazy.xml b/features/verifysession/impl/src/main/res/values/localazy.xml index df9e6284d5..6bab676981 100644 --- a/features/verifysession/impl/src/main/res/values/localazy.xml +++ b/features/verifysession/impl/src/main/res/values/localazy.xml @@ -11,13 +11,14 @@ "Use another device" "Waiting on other device…" "Something doesn’t seem right. Either the request timed out or the request was denied." - "Confirm that the emojis below match those shown on your other session." + "Confirm that the emojis below match those shown on your other device." "Compare emojis" "Confirm that the emojis below match those shown on the other user’s device." "Confirm that the numbers below match those shown on your other session." "Compare numbers" - "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted." + "Now you can read or send messages securely on your other device." "Now you can trust the identity of this user when sending or receiving messages." + "Device verified" "Enter recovery key" "Either the request timed out, the request was denied, or there was a verification mismatch." "Prove it’s you in order to access your encrypted message history." @@ -44,7 +45,7 @@ "For extra security, another user wants to verify your identity. You’ll be shown a set of emojis to compare." "You should see a popup on the other device. Start the verification from there now." "Start verification on the other device" - "Waiting for the other device" + "Start verification on the other device" "Waiting for the other user" "Once accepted you’ll be able to continue with the verification." "Accept the request to start the verification process in your other session to continue." 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 06c4956ea9..2bf7116990 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 @@ -12,6 +12,7 @@ import io.element.android.features.verifysession.impl.ui.aEmojisSessionVerificat import io.element.android.libraries.dateformatter.api.DateFormatter import io.element.android.libraries.dateformatter.test.FakeDateFormatter import io.element.android.libraries.matrix.api.core.FlowId +import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.VerificationFlowState @@ -293,13 +294,14 @@ class IncomingVerificationPresenterTest { private val anIncomingSessionVerificationRequest = VerificationRequest.Incoming.OtherSession( details = SessionVerificationRequestDetails( - senderProfile = SessionVerificationRequestDetails.SenderProfile( + senderProfile = MatrixUser( userId = A_USER_ID, - displayName = "a device name", + displayName = "a user name", avatarUrl = null, ), flowId = FlowId("flowId"), deviceId = A_DEVICE_ID, + deviceDisplayName = "a device name", firstSeenTimestamp = A_TIMESTAMP, ) ) 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 1e1c629a66..6b61e05689 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 @@ -62,7 +62,7 @@ class IncomingVerificationViewTest { eventSink = eventsRecorder ), ) - rule.clickOn(CommonStrings.action_start) + rule.clickOn(CommonStrings.action_start_verification) eventsRecorder.assertSingle(IncomingVerificationViewEvents.StartVerification) } diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileNode.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileNode.kt index 140a6258e2..41369dda07 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileNode.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileNode.kt @@ -15,13 +15,13 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs @ContributesNode(AppScope::class) -@Inject +@AssistedInject class ViewFileNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 e22ae371cb..fb330327f3 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 @@ -16,13 +16,13 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -@Inject +@AssistedInject class ViewFilePresenter( @Assisted("path") val path: String, @Assisted("name") val name: String, diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderNode.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderNode.kt index fce49faffb..4c57ea4135 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderNode.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderNode.kt @@ -15,14 +15,14 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.viewfolder.impl.model.Item import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs @ContributesNode(AppScope::class) -@Inject +@AssistedInject class ViewFolderNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderPresenter.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderPresenter.kt index 8e392855d8..0c5ab61705 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderPresenter.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderPresenter.kt @@ -15,14 +15,15 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.features.viewfolder.impl.model.Item import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.meta.BuildMeta +import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList -@Inject +@AssistedInject class ViewFolderPresenter( @Assisted val canGoUp: Boolean, @Assisted val path: String, @@ -36,7 +37,7 @@ class ViewFolderPresenter( @Composable override fun present(): ViewFolderState { - var content by remember { mutableStateOf(persistentListOf()) } + var content by remember { mutableStateOf>(persistentListOf()) } val title = remember { buildString { if (path.contains(buildMeta.applicationId)) { @@ -49,7 +50,7 @@ class ViewFolderPresenter( content = buildList { if (canGoUp) add(Item.Parent) addAll(folderExplorer.getItems(path)) - }.toPersistentList() + }.toImmutableList() } return ViewFolderState( title = title, diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderFlowNode.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderFlowNode.kt index 2eb6712364..d57824f2fd 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderFlowNode.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderFlowNode.kt @@ -19,7 +19,7 @@ import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.viewfolder.api.ViewFolderEntryPoint import io.element.android.features.viewfolder.impl.file.ViewFileNode @@ -33,7 +33,7 @@ import io.element.android.libraries.architecture.inputs import kotlinx.parcelize.Parcelize @ContributesNode(AppScope::class) -@Inject +@AssistedInject class ViewFolderFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/gradle.properties b/gradle.properties index 18399ccf06..a2c7228ed2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -54,3 +54,7 @@ ksp.allow.all.target.configuration=false # Used to prevent detekt from reusing invalid cached rules detekt.use.worker.api=true + +# Let test include roborazzi verification +# https://github.com/takahirom/roborazzi?tab=readme-ov-file#roborazzitest +roborazzi.test.verify=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ea78e146b4..0d4cb342dc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,9 +6,9 @@ # We cannot use 8.12.+ since it breaks F-Droid build (see https://github.com/element-hq/element-x-android/issues/3420#issuecomment-3199571010) android_gradle_plugin = "8.11.1" # When updateing this, please also update the version in the file ./idea/kotlinc.xml -kotlin = "2.2.10" +kotlin = "2.2.20" kotlinpoet = "2.2.0" -ksp = "2.2.10-2.0.2" +ksp = "2.2.20-2.0.2" firebaseAppDistribution = "5.1.1" # AndroidX @@ -32,6 +32,7 @@ accompanist = "0.37.3" # Test test_core = "1.7.0" +roborazzi = "1.50.0" # Jetbrain datetime = "0.7.1" @@ -44,14 +45,14 @@ showkase = "1.0.5" appyx = "1.7.1" sqldelight = "2.1.0" wysiwyg = "2.39.0" -telephoto = "0.17.0" +telephoto = "0.18.0" haze = "1.6.10" # Dependency analysis dependencyAnalysis = "3.0.4" # DI -metro = "0.6.4" +metro = "0.6.8" # Auto service autoservice = "1.1.1" @@ -149,7 +150,7 @@ test_corektx = { module = "androidx.test:core-ktx", version.ref = "test_core" } test_arch_core = "androidx.arch.core:core-testing:2.2.0" test_junit = "junit:junit:4.13.2" test_runner = "androidx.test:runner:1.7.0" -test_mockk = "io.mockk:mockk:1.14.5" +test_mockk = "io.mockk:mockk:1.14.6" test_konsist = "com.lemonappdev:konsist:0.17.3" test_turbine = "app.cash.turbine:turbine:1.2.1" test_truth = "com.google.truth:truth:1.4.5" @@ -166,7 +167,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:25.9.23" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.10.7" # Others coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } @@ -174,7 +175,6 @@ coil_network_okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version coil_compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" } coil_gif = { module = "io.coil-kt.coil3:coil-gif", version.ref = "coil" } coil_test = { module = "io.coil-kt.coil3:coil-test", version.ref = "coil" } -compound = { module = "io.element.android:compound-android", version = "25.7.4" } datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" } serialization_json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization_json" } kotlinx_collections_immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0" @@ -182,7 +182,7 @@ showkase = { module = "com.airbnb.android:showkase", version.ref = "showkase" } showkase_processor = { module = "com.airbnb.android:showkase-processor", version.ref = "showkase" } jsoup = "org.jsoup:jsoup:1.21.2" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } -molecule-runtime = "app.cash.molecule:molecule-runtime:2.1.0" +molecule-runtime = "app.cash.molecule:molecule-runtime:2.2.0" timber = "com.jakewharton.timber:timber:5.0.1" matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" } matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" } @@ -190,13 +190,13 @@ sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", ver 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.10.0" -sqlite = "androidx.sqlite:sqlite-ktx:2.6.0" +sqlite = "androidx.sqlite:sqlite-ktx:2.6.1" unifiedpush = "org.unifiedpush.android:connector:3.0.10" vanniktech_blurhash = "com.vanniktech:blurhash:0.3.0" telephoto_zoomableimage = { module = "me.saket.telephoto:zoomable-image-coil", version.ref = "telephoto" } telephoto_flick = { module = "me.saket.telephoto:flick-android", version.ref = "telephoto" } statemachine = "com.freeletics.flowredux:compose:1.2.2" -maplibre = "org.maplibre.gl:android-sdk:11.13.5" +maplibre = "org.maplibre.gl:android-sdk:12.0.0" maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:3.0.2" maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:3.0.2" opusencoder = "io.element.android:opusencoder:1.2.0" @@ -205,8 +205,8 @@ haze = { module = "dev.chrisbanes.haze:haze", version.ref = "haze" } haze_materials = { module = "dev.chrisbanes.haze:haze-materials", version.ref = "haze" } # Analytics -posthog = "com.posthog:posthog-android:3.21.2" -sentry = "io.sentry:sentry-android:8.22.0" +posthog = "com.posthog:posthog-android:3.23.0" +sentry = "io.sentry:sentry-android:8.23.0" # main branch can be tested replacing the version with main-SNAPSHOT matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.28.0" @@ -215,11 +215,10 @@ matrix_emojibase_bindings = "io.element.android:emojibase-bindings:1.4.3" sigpwned_emoji4j = "com.sigpwned:emoji4j-core:16.0.0" # Di -inject = "javax.inject:javax.inject:1" metro_runtime = { module = "dev.zacsweers.metro:runtime", version.ref = "metro" } # Element Call -element_call_embedded = "io.element.android:element-call-embedded:0.16.0-rc.4" +element_call_embedded = "io.element.android:element-call-embedded:0.16.0" # Auto services google_autoservice = { module = "com.google.auto.service:auto-service", version.ref = "autoservice" } @@ -228,6 +227,11 @@ google_autoservice_annotations = { module = "com.google.auto.service:auto-servic # Miscellaneous androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" } +# Test +test_roborazzi = { module = "io.github.takahirom.roborazzi:roborazzi", version.ref = "roborazzi" } +test_roborazzi_compose = { module = "io.github.takahirom.roborazzi:roborazzi-compose", version.ref = "roborazzi" } +test_roborazzi_junit = { module = "io.github.takahirom.roborazzi:roborazzi-junit-rule", version.ref = "roborazzi" } + [bundles] [plugins] @@ -237,13 +241,15 @@ kotlin_android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin_jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin_serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } +# Note: used in DependencyInjectionExtensions.kt metro = { id = "dev.zacsweers.metro", version.ref = "metro" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } ktlint = "org.jlleitschuh.gradle.ktlint:13.1.0" dependencygraph = "com.savvasdalkitsis.module-dependency-graph:0.12" -dependencycheck = "org.owasp.dependencycheck:12.1.5" +dependencycheck = "org.owasp.dependencycheck:12.1.6" dependencyanalysis = { id = "com.autonomousapps.dependency-analysis", version.ref = "dependencyAnalysis" } paparazzi = "app.cash.paparazzi:2.0.0-alpha02" +roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" } sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } firebaseAppDistribution = { id = "com.google.firebase.appdistribution", version.ref = "firebaseAppDistribution" } knit = { id = "org.jetbrains.kotlinx.knit", version = "0.5.0" } diff --git a/libraries/accountselect/api/build.gradle.kts b/libraries/accountselect/api/build.gradle.kts new file mode 100644 index 0000000000..7e0ce303f9 --- /dev/null +++ b/libraries/accountselect/api/build.gradle.kts @@ -0,0 +1,18 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.libraries.accountselect.api" +} + +dependencies { + implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) +} diff --git a/libraries/accountselect/api/src/main/kotlin/io/element/android/libraries/accountselect/api/AccountSelectEntryPoint.kt b/libraries/accountselect/api/src/main/kotlin/io/element/android/libraries/accountselect/api/AccountSelectEntryPoint.kt new file mode 100644 index 0000000000..72da3491de --- /dev/null +++ b/libraries/accountselect/api/src/main/kotlin/io/element/android/libraries/accountselect/api/AccountSelectEntryPoint.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2025 New Vector 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.accountselect.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.matrix.api.core.SessionId + +interface AccountSelectEntryPoint : FeatureEntryPoint { + fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder + + interface NodeBuilder { + fun callback(callback: Callback): NodeBuilder + fun build(): Node + } + + interface Callback : Plugin { + fun onSelectAccount(sessionId: SessionId) + fun onCancel() + } +} diff --git a/libraries/accountselect/impl/build.gradle.kts b/libraries/accountselect/impl/build.gradle.kts new file mode 100644 index 0000000000..ea1fbd52ad --- /dev/null +++ b/libraries/accountselect/impl/build.gradle.kts @@ -0,0 +1,35 @@ +import extension.setupDependencyInjection +import extension.testCommonDependencies + +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +plugins { + id("io.element.android-compose-library") +} + +android { + namespace = "io.element.android.libraries.accountselect.impl" +} + +setupDependencyInjection() + +dependencies { + implementation(projects.libraries.core) + implementation(projects.libraries.androidutils) + implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) + implementation(projects.libraries.matrixui) + implementation(projects.libraries.sessionStorage.api) + implementation(projects.libraries.designsystem) + implementation(projects.libraries.uiStrings) + api(projects.libraries.accountselect.api) + + testCommonDependencies(libs) + testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.sessionStorage.test) +} diff --git a/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectNode.kt b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectNode.kt new file mode 100644 index 0000000000..5478d9fe43 --- /dev/null +++ b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectNode.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2025 New Vector 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.accountselect.impl + +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.libraries.accountselect.api.AccountSelectEntryPoint +import io.element.android.libraries.matrix.api.core.SessionId + +@ContributesNode(AppScope::class) +@AssistedInject +class AccountSelectNode( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + private val presenter: AccountSelectPresenter, +) : Node(buildContext, plugins = plugins) { + private val callbacks = plugins.filterIsInstance() + + private fun onDismiss() { + callbacks.forEach { it.onCancel() } + } + + private fun onSelectAccount(sessionId: SessionId) { + callbacks.forEach { it.onSelectAccount(sessionId) } + } + + @Composable + override fun View(modifier: Modifier) { + val state = presenter.present() + AccountSelectView( + state = state, + onDismiss = ::onDismiss, + onSelectAccount = ::onSelectAccount, + modifier = modifier, + ) + } +} diff --git a/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectPresenter.kt b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectPresenter.kt new file mode 100644 index 0000000000..f69708cc9d --- /dev/null +++ b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectPresenter.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2025 New Vector 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.accountselect.impl + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.produceState +import dev.zacsweers.metro.Inject +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.sessionstorage.api.SessionStore +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList + +@Inject +class AccountSelectPresenter( + private val sessionStore: SessionStore, +) : Presenter { + @Composable + override fun present(): AccountSelectState { + val accounts by produceState>(persistentListOf()) { + // Do not use sessionStore.sessionsFlow() to not make it change when an account is selected. + value = sessionStore.getAllSessions() + .map { + MatrixUser( + userId = UserId(it.userId), + displayName = it.userDisplayName, + avatarUrl = it.userAvatarUrl, + ) + } + .toImmutableList() + } + + return AccountSelectState( + accounts = accounts, + ) + } +} diff --git a/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectState.kt b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectState.kt new file mode 100644 index 0000000000..feaedaf90d --- /dev/null +++ b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectState.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2025 New Vector 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.accountselect.impl + +import io.element.android.libraries.matrix.api.user.MatrixUser +import kotlinx.collections.immutable.ImmutableList + +data class AccountSelectState( + val accounts: ImmutableList, +) diff --git a/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectStateProvider.kt b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectStateProvider.kt new file mode 100644 index 0000000000..5d99bf3b0c --- /dev/null +++ b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectStateProvider.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2025 New Vector 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.accountselect.impl + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.ui.components.aMatrixUserList +import kotlinx.collections.immutable.toImmutableList + +open class AccountSelectStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + anAccountSelectState(), + anAccountSelectState(accounts = aMatrixUserList()), + ) +} + +private fun anAccountSelectState( + accounts: List = listOf(), +) = AccountSelectState( + accounts = accounts.toImmutableList(), +) diff --git a/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectView.kt b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectView.kt new file mode 100644 index 0000000000..b589df23f6 --- /dev/null +++ b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectView.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2025 New Vector 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.accountselect.impl + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.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.HorizontalDivider +import io.element.android.libraries.designsystem.theme.components.Scaffold +import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.ui.components.MatrixUserRow +import io.element.android.libraries.ui.strings.CommonStrings + +@Suppress("MultipleEmitters") // False positive +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AccountSelectView( + state: AccountSelectState, + onSelectAccount: (SessionId) -> Unit, + onDismiss: () -> Unit, + modifier: Modifier = Modifier, +) { + BackHandler(onBack = { onDismiss() }) + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + titleStr = stringResource(CommonStrings.common_select_account), + navigationIcon = { + BackButton(onClick = { onDismiss() }) + }, + ) + } + ) { paddingValues -> + Column( + Modifier + .padding(paddingValues) + .consumeWindowInsets(paddingValues) + ) { + LazyColumn { + items(state.accounts, key = { it.userId }) { matrixUser -> + Column { + MatrixUserRow( + modifier = Modifier + .fillMaxWidth() + .clickable { + onSelectAccount(matrixUser.userId) + } + .padding(vertical = 8.dp), + matrixUser = matrixUser, + ) + HorizontalDivider() + } + } + } + } + } +} + +@PreviewsDayNight +@Composable +internal fun AccountSelectViewPreview(@PreviewParameter(AccountSelectStateProvider::class) state: AccountSelectState) = ElementPreview { + AccountSelectView( + state = state, + onSelectAccount = {}, + onDismiss = {}, + ) +} diff --git a/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/DefaultAccountSelectEntryPoint.kt b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/DefaultAccountSelectEntryPoint.kt new file mode 100644 index 0000000000..baf5ecd5b3 --- /dev/null +++ b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/DefaultAccountSelectEntryPoint.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2025 New Vector 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.accountselect.impl + +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.ContributesBinding +import dev.zacsweers.metro.Inject +import io.element.android.libraries.accountselect.api.AccountSelectEntryPoint +import io.element.android.libraries.architecture.createNode + +@ContributesBinding(AppScope::class) +@Inject +class DefaultAccountSelectEntryPoint : AccountSelectEntryPoint { + override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): AccountSelectEntryPoint.NodeBuilder { + val plugins = ArrayList() + + return object : AccountSelectEntryPoint.NodeBuilder { + override fun callback(callback: AccountSelectEntryPoint.Callback): AccountSelectEntryPoint.NodeBuilder { + plugins += callback + return this + } + + override fun build(): Node { + return parentNode.createNode(buildContext, plugins) + } + } + } +} diff --git a/libraries/accountselect/impl/src/test/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectPresenterTest.kt b/libraries/accountselect/impl/src/test/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectPresenterTest.kt new file mode 100644 index 0000000000..27a8d7d9cf --- /dev/null +++ b/libraries/accountselect/impl/src/test/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectPresenterTest.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2025 New Vector 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.accountselect.impl + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.user.MatrixUser +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.sessionstorage.api.SessionStore +import io.element.android.libraries.sessionstorage.test.InMemorySessionStore +import io.element.android.libraries.sessionstorage.test.aSessionData +import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.test +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test + +class AccountSelectPresenterTest { + @get:Rule + val warmUpRule = WarmUpRule() + + @Test + fun `present - initial state`() = runTest { + val presenter = createAccountSelectPresenter() + presenter.test { + val initialState = awaitItem() + assertThat(initialState.accounts).isEmpty() + } + } + + @Test + fun `present - multiple accounts case`() = runTest { + val presenter = createAccountSelectPresenter( + sessionStore = InMemorySessionStore( + initialList = listOf( + aSessionData(sessionId = A_SESSION_ID.value), + aSessionData( + sessionId = A_SESSION_ID_2.value, + userDisplayName = "Bob", + userAvatarUrl = "avatarUrl", + ), + ) + ) + ) + presenter.test { + skipItems(1) + val initialState = awaitItem() + assertThat(initialState.accounts).hasSize(2) + val firstAccount = initialState.accounts[0] + assertThat(firstAccount).isEqualTo( + MatrixUser( + userId = A_SESSION_ID, + displayName = null, + avatarUrl = null, + ) + ) + val secondAccount = initialState.accounts[1] + assertThat(secondAccount).isEqualTo( + MatrixUser( + userId = A_SESSION_ID_2, + displayName = "Bob", + avatarUrl = "avatarUrl", + ) + ) + } + } +} + +internal fun createAccountSelectPresenter( + sessionStore: SessionStore = InMemorySessionStore(), +) = AccountSelectPresenter( + sessionStore = sessionStore, +) diff --git a/libraries/accountselect/impl/src/test/kotlin/io/element/android/libraries/accountselect/impl/DefaultAccountSelectEntryPointTest.kt b/libraries/accountselect/impl/src/test/kotlin/io/element/android/libraries/accountselect/impl/DefaultAccountSelectEntryPointTest.kt new file mode 100644 index 0000000000..d61dcc89ba --- /dev/null +++ b/libraries/accountselect/impl/src/test/kotlin/io/element/android/libraries/accountselect/impl/DefaultAccountSelectEntryPointTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2025 New Vector 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.accountselect.impl + +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import com.bumble.appyx.core.modality.BuildContext +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.accountselect.api.AccountSelectEntryPoint +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.tests.testutils.lambda.lambdaError +import io.element.android.tests.testutils.node.TestParentNode +import org.junit.Rule +import org.junit.Test + +class DefaultAccountSelectEntryPointTest { + @get:Rule + val instantTaskExecutorRule = InstantTaskExecutorRule() + + @Test + fun `test node builder`() { + val entryPoint = DefaultAccountSelectEntryPoint() + val parentNode = TestParentNode.create { buildContext, plugins -> + AccountSelectNode( + buildContext = buildContext, + plugins = plugins, + presenter = createAccountSelectPresenter(), + ) + } + val callback = object : AccountSelectEntryPoint.Callback { + override fun onSelectAccount(sessionId: SessionId) = lambdaError() + override fun onCancel() = lambdaError() + } + val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) + .callback(callback) + .build() + assertThat(result).isInstanceOf(AccountSelectNode::class.java) + assertThat(result.plugins).contains(callback) + } +} diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/appyx/DelegateTransitionHandler.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/appyx/DelegateTransitionHandler.kt new file mode 100644 index 0000000000..642ff6fc3a --- /dev/null +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/appyx/DelegateTransitionHandler.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2025 New Vector 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.architecture.appyx + +import android.annotation.SuppressLint +import androidx.compose.animation.core.Transition +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.navigation.transition.ModifierTransitionHandler +import com.bumble.appyx.core.navigation.transition.TransitionDescriptor + +/** + * A [ModifierTransitionHandler] that delegates the creation of the modifier to another handler + * based on the [NavTarget]. The idea is to allow different transitions for different [NavTarget]s. + */ +class DelegateTransitionHandler( + private val handlerProvider: (NavTarget) -> ModifierTransitionHandler, +) : ModifierTransitionHandler() { + @SuppressLint("ModifierFactoryExtensionFunction") + override fun createModifier(modifier: Modifier, transition: Transition, descriptor: TransitionDescriptor): Modifier { + return handlerProvider(descriptor.element).createModifier(modifier, transition, descriptor) + } +} + +@Composable +fun rememberDelegateTransitionHandler( + handlerProvider: (NavTarget) -> ModifierTransitionHandler, +): ModifierTransitionHandler = + remember(handlerProvider) { DelegateTransitionHandler(handlerProvider = handlerProvider) } diff --git a/libraries/compound/build.gradle.kts b/libraries/compound/build.gradle.kts new file mode 100644 index 0000000000..cbdb09d451 --- /dev/null +++ b/libraries/compound/build.gradle.kts @@ -0,0 +1,29 @@ +import extension.testCommonDependencies + +/* + * Copyright 2022, 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +plugins { + id("io.element.android-compose-library") + alias(libs.plugins.roborazzi) +} + +android { + namespace = "io.element.android.compound" + + testOptions { + unitTests.isIncludeAndroidResources = true + } + + dependencies { + implementation(libs.showkase) + testCommonDependencies(libs) + testImplementation(libs.test.roborazzi) + testImplementation(libs.test.roborazzi.compose) + testImplementation(libs.test.roborazzi.junit) + } +} diff --git a/libraries/compound/screenshots/Avatar Colors - Dark.png b/libraries/compound/screenshots/Avatar Colors - Dark.png new file mode 100644 index 0000000000..5cf6cbda89 --- /dev/null +++ b/libraries/compound/screenshots/Avatar Colors - Dark.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:caf2de32caf0fa5368da899297e2eabd5a0c6891dd94f81295ef8a933d79ce16 +size 10751 diff --git a/libraries/compound/screenshots/Avatar Colors - Light.png b/libraries/compound/screenshots/Avatar Colors - Light.png new file mode 100644 index 0000000000..7069762082 --- /dev/null +++ b/libraries/compound/screenshots/Avatar Colors - Light.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:80e44e94d7b23af2ec4fd1c5a871851ae2567b40e478b30145de199076f20e95 +size 11296 diff --git a/libraries/compound/screenshots/Compound Icons - Dark.png b/libraries/compound/screenshots/Compound Icons - Dark.png new file mode 100644 index 0000000000..acac494f89 --- /dev/null +++ b/libraries/compound/screenshots/Compound Icons - Dark.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6bc48b9f792da838f9fc2c2a630cbbbb906de851a138cc1ac8b7bf67b801ad84 +size 211300 diff --git a/libraries/compound/screenshots/Compound Icons - Light.png b/libraries/compound/screenshots/Compound Icons - Light.png new file mode 100644 index 0000000000..c6b76c46bb --- /dev/null +++ b/libraries/compound/screenshots/Compound Icons - Light.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e85fd2c67ff42829b8580ab5c0c2af1fee40973f5c0b34ef7de00e3663cee8e4 +size 223041 diff --git a/libraries/compound/screenshots/Compound Icons - Rtl.png b/libraries/compound/screenshots/Compound Icons - Rtl.png new file mode 100644 index 0000000000..ce2ffe2e88 --- /dev/null +++ b/libraries/compound/screenshots/Compound Icons - Rtl.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:406b62991171a146e891f95bcad0321ebc60cf1fe2cabc9caedbb17fb062af13 +size 224320 diff --git a/libraries/compound/screenshots/Compound Semantic Colors - Dark HC.png b/libraries/compound/screenshots/Compound Semantic Colors - Dark HC.png new file mode 100644 index 0000000000..4890b91886 --- /dev/null +++ b/libraries/compound/screenshots/Compound Semantic Colors - Dark HC.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:578e9b5a38791e2686a7b9ba5c461eb1d1fb29dfbe950bf46c113ad75ceac175 +size 327758 diff --git a/libraries/compound/screenshots/Compound Semantic Colors - Dark.png b/libraries/compound/screenshots/Compound Semantic Colors - Dark.png new file mode 100644 index 0000000000..4cc125b4c8 --- /dev/null +++ b/libraries/compound/screenshots/Compound Semantic Colors - Dark.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4cab40fc0506c8f2a2efafb1199e85f1da3ebacb49b176e9105e3f95175f85ee +size 325565 diff --git a/libraries/compound/screenshots/Compound Semantic Colors - Light HC.png b/libraries/compound/screenshots/Compound Semantic Colors - Light HC.png new file mode 100644 index 0000000000..5a8f5a6b32 --- /dev/null +++ b/libraries/compound/screenshots/Compound Semantic Colors - Light HC.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:174f9d4ee70a29c0c8c2a01a15daeb14281530678ff7d7fb19a208bfd789533a +size 309210 diff --git a/libraries/compound/screenshots/Compound Semantic Colors - Light.png b/libraries/compound/screenshots/Compound Semantic Colors - Light.png new file mode 100644 index 0000000000..f010626dda --- /dev/null +++ b/libraries/compound/screenshots/Compound Semantic Colors - Light.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7598b98462c015f2bf74b3ea3ad95fc0220b2efb9bb81ac56025cf6a158e3f8a +size 308976 diff --git a/libraries/compound/screenshots/Compound Typography.png b/libraries/compound/screenshots/Compound Typography.png new file mode 100644 index 0000000000..095ad6c71d --- /dev/null +++ b/libraries/compound/screenshots/Compound Typography.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac84a7175c4a4897aa28eddcf722b7997c6576f612eb38fa09ffabcf7be11e00 +size 119496 diff --git a/libraries/compound/screenshots/Compound Vector Icons - Dark.png b/libraries/compound/screenshots/Compound Vector Icons - Dark.png new file mode 100644 index 0000000000..2b535c348b --- /dev/null +++ b/libraries/compound/screenshots/Compound Vector Icons - Dark.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6ff6dfdfab51332cad3bdfa351a4d0e305de5f899853575a8514858cc871e904 +size 83609 diff --git a/libraries/compound/screenshots/Compound Vector Icons - Light.png b/libraries/compound/screenshots/Compound Vector Icons - Light.png new file mode 100644 index 0000000000..bc29dcd24d --- /dev/null +++ b/libraries/compound/screenshots/Compound Vector Icons - Light.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca38f5f23c282a6dc4c01a54705f71bf8c927aeac9c1df0a1f3abe50c10b1b85 +size 89336 diff --git a/libraries/compound/screenshots/ForcedDarkElementTheme.png b/libraries/compound/screenshots/ForcedDarkElementTheme.png new file mode 100644 index 0000000000..d7182aa47c --- /dev/null +++ b/libraries/compound/screenshots/ForcedDarkElementTheme.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:03bdb2f3de01d40b8f85ba87b395cdab2e5225aded2f40a0892077798bca6066 +size 22328 diff --git a/libraries/compound/screenshots/Legacy Colors.png b/libraries/compound/screenshots/Legacy Colors.png new file mode 100644 index 0000000000..2ecdda2ed7 --- /dev/null +++ b/libraries/compound/screenshots/Legacy Colors.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e9d676a1ef20a7228e985f62d346265ff9c31d1860a219540f012c063c9345e +size 33652 diff --git a/libraries/compound/screenshots/Material Typography.png b/libraries/compound/screenshots/Material Typography.png new file mode 100644 index 0000000000..6c22c364b9 --- /dev/null +++ b/libraries/compound/screenshots/Material Typography.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d27f813acc9e8dc3960f5205809c8c0d2ba0fa51fd9e3ca07964b866b125e87d +size 110171 diff --git a/libraries/compound/screenshots/Material3 Colors - Dark HC.png b/libraries/compound/screenshots/Material3 Colors - Dark HC.png new file mode 100644 index 0000000000..4cc7609c89 --- /dev/null +++ b/libraries/compound/screenshots/Material3 Colors - Dark HC.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85969829577e158bdd1d0f21c8b3a2334dcde79cb50d5e2331d06d5423332be2 +size 160754 diff --git a/libraries/compound/screenshots/Material3 Colors - Dark.png b/libraries/compound/screenshots/Material3 Colors - Dark.png new file mode 100644 index 0000000000..3738cf6a79 --- /dev/null +++ b/libraries/compound/screenshots/Material3 Colors - Dark.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca64da1dc373dc49503ee525ae3331e73d0ab12053993a1ef19dcab1e67b08c4 +size 159123 diff --git a/libraries/compound/screenshots/Material3 Colors - Light HC.png b/libraries/compound/screenshots/Material3 Colors - Light HC.png new file mode 100644 index 0000000000..8d64857d13 --- /dev/null +++ b/libraries/compound/screenshots/Material3 Colors - Light HC.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:01622bca20a132ec5a874fc1a2d0ffd45e7ce6d7849c4d607d79c7bc51d6c6a9 +size 163322 diff --git a/libraries/compound/screenshots/Material3 Colors - Light.png b/libraries/compound/screenshots/Material3 Colors - Light.png new file mode 100644 index 0000000000..a1d5d1f2ce --- /dev/null +++ b/libraries/compound/screenshots/Material3 Colors - Light.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87c0c4ff42d17137d554708ce33f40f214ec608eca4ca87af0b2adab63de6bb7 +size 162891 diff --git a/libraries/compound/screenshots/MaterialText Colors.png b/libraries/compound/screenshots/MaterialText Colors.png new file mode 100644 index 0000000000..f8f77ccca2 --- /dev/null +++ b/libraries/compound/screenshots/MaterialText Colors.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4be10c3bb9900d27a3b406eca0cb902b0ff9cdf90e8e3cf1ae7760aa7c5d47d9 +size 377446 diff --git a/libraries/compound/screenshots/MaterialYou Theme - Dark.png b/libraries/compound/screenshots/MaterialYou Theme - Dark.png new file mode 100644 index 0000000000..8c5bc9d1ef --- /dev/null +++ b/libraries/compound/screenshots/MaterialYou Theme - Dark.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c166e5371bb1922a9c016438a3cdfd0d68197237969d53a04f92baf6d53c4ac0 +size 164925 diff --git a/libraries/compound/screenshots/MaterialYou Theme - Light.png b/libraries/compound/screenshots/MaterialYou Theme - Light.png new file mode 100644 index 0000000000..70cccac69b --- /dev/null +++ b/libraries/compound/screenshots/MaterialYou Theme - Light.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d936948cbad69d6935f2d2738d33682a55f044dfb1af8b5c9b8323c5f4318971 +size 163558 diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/annotations/CoreColorToken.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/annotations/CoreColorToken.kt new file mode 100644 index 0000000000..a56ecb38a8 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/annotations/CoreColorToken.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2023, 2024 New Vector 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.compound.annotations + +@RequiresOptIn( + message = "This is a Core color token, which should only be used to declare semantic colors, otherwise it" + + " would look the same on both light and dark modes. Only use it as is if you know what you are doing." +) +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS) +annotation class CoreColorToken diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/previews/ColorListPreview.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/previews/ColorListPreview.kt new file mode 100644 index 0000000000..715da1ebc3 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/previews/ColorListPreview.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2023, 2024 New Vector 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.compound.previews + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import kotlinx.collections.immutable.ImmutableMap +import kotlin.math.ceil + +@Composable +fun ColorListPreview( + backgroundColor: Color, + foregroundColor: Color, + colors: ImmutableMap, + modifier: Modifier = Modifier, + numColumns: Int = 1, +) { + Row( + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + colors.keys + .chunked(ceil(colors.keys.size / numColumns.toDouble()).toInt()) + .forEach { subList -> + Column( + modifier = Modifier + .background(color = backgroundColor) + .weight(1f) + ) { + subList.forEach { name -> + val color = colors[name]!! + ColorPreview( + backgroundColor = backgroundColor, + foregroundColor = foregroundColor, + name = name, + color = color + ) + } + Spacer(modifier = Modifier.height(2.dp)) + } + } + } +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/previews/ColorPreview.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/previews/ColorPreview.kt new file mode 100644 index 0000000000..3248adeedf --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/previews/ColorPreview.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2023, 2024 New Vector 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.compound.previews + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.element.android.compound.utils.toHrf + +@Composable +fun ColorPreview( + backgroundColor: Color, + foregroundColor: Color, + name: String, + color: Color, + modifier: Modifier = Modifier, +) { + Column(modifier = modifier.fillMaxWidth()) { + Text( + modifier = Modifier.padding(horizontal = 10.dp), + text = name + " " + color.toHrf(), + fontSize = 6.sp, + color = foregroundColor, + ) + val backgroundBrush = Brush.linearGradient( + listOf( + backgroundColor, + foregroundColor, + ) + ) + Row( + modifier = Modifier.background(backgroundBrush) + ) { + repeat(2) { + Box( + modifier = Modifier + .padding(1.dp) + .background(Color.White) + .background(color = color) + .height(10.dp) + .weight(1f) + ) + } + } + } +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/previews/ColorsSchemePreview.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/previews/ColorsSchemePreview.kt new file mode 100644 index 0000000000..cf660ba236 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/previews/ColorsSchemePreview.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2023, 2024 New Vector 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.compound.previews + +import androidx.compose.material3.ColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import kotlinx.collections.immutable.persistentMapOf + +@Composable +internal fun ColorsSchemePreview( + backgroundColor: Color, + foregroundColor: Color, + colorScheme: ColorScheme, + modifier: Modifier = Modifier, +) { + val colors = persistentMapOf( + "primary" to colorScheme.primary, + "onPrimary" to colorScheme.onPrimary, + "primaryContainer" to colorScheme.primaryContainer, + "onPrimaryContainer" to colorScheme.onPrimaryContainer, + "inversePrimary" to colorScheme.inversePrimary, + "secondary" to colorScheme.secondary, + "onSecondary" to colorScheme.onSecondary, + "secondaryContainer" to colorScheme.secondaryContainer, + "onSecondaryContainer" to colorScheme.onSecondaryContainer, + "tertiary" to colorScheme.tertiary, + "onTertiary" to colorScheme.onTertiary, + "tertiaryContainer" to colorScheme.tertiaryContainer, + "onTertiaryContainer" to colorScheme.onTertiaryContainer, + "background" to colorScheme.background, + "onBackground" to colorScheme.onBackground, + "surface" to colorScheme.surface, + "onSurface" to colorScheme.onSurface, + "surfaceVariant" to colorScheme.surfaceVariant, + "onSurfaceVariant" to colorScheme.onSurfaceVariant, + "surfaceTint" to colorScheme.surfaceTint, + "inverseSurface" to colorScheme.inverseSurface, + "inverseOnSurface" to colorScheme.inverseOnSurface, + "error" to colorScheme.error, + "onError" to colorScheme.onError, + "errorContainer" to colorScheme.errorContainer, + "onErrorContainer" to colorScheme.onErrorContainer, + "outline" to colorScheme.outline, + "outlineVariant" to colorScheme.outlineVariant, + "scrim" to colorScheme.scrim, + ) + ColorListPreview( + backgroundColor = backgroundColor, + foregroundColor = foregroundColor, + colors = colors, + modifier = modifier, + ) +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/previews/CompoundIconsPreview.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/previews/CompoundIconsPreview.kt new file mode 100644 index 0000000000..0ee0546ca4 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/previews/CompoundIconsPreview.kt @@ -0,0 +1,143 @@ +/* + * Copyright 2023, 2025 New Vector 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.compound.previews + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList + +@Preview(widthDp = 730, heightDp = 1800) +@Composable +internal fun IconsCompoundPreviewLight() = ElementTheme { + IconsCompoundPreview() +} + +@Preview(widthDp = 730, heightDp = 1800) +@Composable +internal fun IconsCompoundPreviewRtl() = ElementTheme { + CompositionLocalProvider( + LocalLayoutDirection provides LayoutDirection.Rtl, + ) { + IconsCompoundPreview( + title = "Compound Icons Rtl", + ) + } +} + +@Preview(widthDp = 730, heightDp = 1800) +@Composable +internal fun IconsCompoundPreviewDark() = ElementTheme(darkTheme = true) { + IconsCompoundPreview() +} + +@Composable +private fun IconsCompoundPreview( + title: String = "Compound Icons", +) { + val context = LocalContext.current + val content: Sequence<@Composable ColumnScope.() -> Unit> = sequence { + for (icon in CompoundIcons.allResIds) { + yield { + Icon( + modifier = Modifier.size(32.dp), + imageVector = ImageVector.vectorResource(icon), + contentDescription = null, + ) + Text( + modifier = Modifier.fillMaxWidth(), + text = context.resources.getResourceEntryName(icon) + .removePrefix("ic_compound_") + .replace("_", " "), + textAlign = TextAlign.Center, + style = ElementTheme.typography.fontBodyXsMedium, + color = ElementTheme.colors.textSecondary, + ) + } + } + } + IconsPreview( + title = title, + content = content.toImmutableList(), + ) +} + +@Composable +internal fun IconsPreview( + title: String, + content: ImmutableList<@Composable ColumnScope.() -> Unit>, +) = Surface { + Column( + modifier = Modifier + .background(MaterialTheme.colorScheme.surfaceVariant) + .padding(16.dp) + .width(IntrinsicSize.Max), + verticalArrangement = Arrangement.spacedBy(6.dp), + ) { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp), + style = ElementTheme.typography.fontHeadingSmMedium, + text = title, + textAlign = TextAlign.Center, + ) + content.chunked(10).forEach { chunk -> + Row( + modifier = Modifier.height(IntrinsicSize.Max), + // Keep same order of icons for an easier comparison of previews + horizontalArrangement = Arrangement.Absolute.Left, + ) { + chunk.forEachIndexed { index, icon -> + Column( + modifier = Modifier + .background(MaterialTheme.colorScheme.background) + .fillMaxHeight() + .width(64.dp) + .padding(4.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + icon() + } + if (index < chunk.size - 1) { + Spacer(modifier = Modifier.width(6.dp)) + } + } + } + } + } +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/previews/SemanticColorsPreview.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/previews/SemanticColorsPreview.kt new file mode 100644 index 0000000000..d641ff39c5 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/previews/SemanticColorsPreview.kt @@ -0,0 +1,205 @@ +/* + * Copyright 2025 New Vector 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.compound.previews + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.compoundColorsHcDark +import kotlinx.collections.immutable.ImmutableMap +import kotlinx.collections.immutable.persistentMapOf + +@Preview(heightDp = 2000) +@Composable +internal fun CompoundSemanticColorsLight() = ElementTheme { + Surface { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + Text("Compound Semantic Colors - Light") + ColorListPreview( + backgroundColor = Color.White, + foregroundColor = Color.Black, + colors = getSemanticColors(), + numColumns = 2, + ) + } + } +} + +@Preview(heightDp = 2000) +@Composable +internal fun CompoundSemanticColorsLightHc() = ElementTheme( + compoundDark = compoundColorsHcDark, +) { + Surface { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + Text("Compound Semantic Colors - Light HC") + ColorListPreview( + backgroundColor = Color.White, + foregroundColor = Color.Black, + colors = getSemanticColors(), + numColumns = 2, + ) + } + } +} + +@Preview(heightDp = 2000) +@Composable +internal fun CompoundSemanticColorsDark() = ElementTheme(darkTheme = true) { + Surface { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + Text("Compound Semantic Colors - Dark") + ColorListPreview( + backgroundColor = Color.White, + foregroundColor = Color.Black, + colors = getSemanticColors(), + numColumns = 2, + ) + } + } +} + +@Preview(heightDp = 2000) +@Composable +internal fun CompoundSemanticColorsDarkHc() = ElementTheme( + darkTheme = true, + compoundDark = compoundColorsHcDark, +) { + Surface { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + Text("Compound Semantic Colors - Dark HC") + ColorListPreview( + backgroundColor = Color.White, + foregroundColor = Color.Black, + colors = getSemanticColors(), + numColumns = 2, + ) + } + } +} + +@Composable +private fun getSemanticColors(): ImmutableMap { + return with(ElementTheme.colors) { + persistentMapOf( + "bgAccentHovered" to bgAccentHovered, + "bgAccentPressed" to bgAccentPressed, + "bgAccentRest" to bgAccentRest, + "bgAccentSelected" to bgAccentSelected, + "bgActionPrimaryDisabled" to bgActionPrimaryDisabled, + "bgActionPrimaryHovered" to bgActionPrimaryHovered, + "bgActionPrimaryPressed" to bgActionPrimaryPressed, + "bgActionPrimaryRest" to bgActionPrimaryRest, + "bgActionSecondaryHovered" to bgActionSecondaryHovered, + "bgActionSecondaryPressed" to bgActionSecondaryPressed, + "bgActionSecondaryRest" to bgActionSecondaryRest, + "bgBadgeAccent" to bgBadgeAccent, + "bgBadgeDefault" to bgBadgeDefault, + "bgBadgeInfo" to bgBadgeInfo, + "bgCanvasDefault" to bgCanvasDefault, + "bgCanvasDefaultLevel1" to bgCanvasDefaultLevel1, + "bgCanvasDisabled" to bgCanvasDisabled, + "bgCriticalHovered" to bgCriticalHovered, + "bgCriticalPrimary" to bgCriticalPrimary, + "bgCriticalSubtle" to bgCriticalSubtle, + "bgCriticalSubtleHovered" to bgCriticalSubtleHovered, + "bgDecorative1" to bgDecorative1, + "bgDecorative2" to bgDecorative2, + "bgDecorative3" to bgDecorative3, + "bgDecorative4" to bgDecorative4, + "bgDecorative5" to bgDecorative5, + "bgDecorative6" to bgDecorative6, + "bgInfoSubtle" to bgInfoSubtle, + "bgSubtlePrimary" to bgSubtlePrimary, + "bgSubtleSecondary" to bgSubtleSecondary, + "bgSubtleSecondaryLevel0" to bgSubtleSecondaryLevel0, + "bgSuccessSubtle" to bgSuccessSubtle, + "borderAccentSubtle" to borderAccentSubtle, + "borderCriticalHovered" to borderCriticalHovered, + "borderCriticalPrimary" to borderCriticalPrimary, + "borderCriticalSubtle" to borderCriticalSubtle, + "borderDisabled" to borderDisabled, + "borderFocused" to borderFocused, + "borderInfoSubtle" to borderInfoSubtle, + "borderInteractiveHovered" to borderInteractiveHovered, + "borderInteractivePrimary" to borderInteractivePrimary, + "borderInteractiveSecondary" to borderInteractiveSecondary, + "borderSuccessSubtle" to borderSuccessSubtle, + "gradientActionStop1" to gradientActionStop1, + "gradientActionStop2" to gradientActionStop2, + "gradientActionStop3" to gradientActionStop3, + "gradientActionStop4" to gradientActionStop4, + "gradientInfoStop1" to gradientInfoStop1, + "gradientInfoStop2" to gradientInfoStop2, + "gradientInfoStop3" to gradientInfoStop3, + "gradientInfoStop4" to gradientInfoStop4, + "gradientInfoStop5" to gradientInfoStop5, + "gradientInfoStop6" to gradientInfoStop6, + "gradientSubtleStop1" to gradientSubtleStop1, + "gradientSubtleStop2" to gradientSubtleStop2, + "gradientSubtleStop3" to gradientSubtleStop3, + "gradientSubtleStop4" to gradientSubtleStop4, + "gradientSubtleStop5" to gradientSubtleStop5, + "gradientSubtleStop6" to gradientSubtleStop6, + "iconAccentPrimary" to iconAccentPrimary, + "iconAccentTertiary" to iconAccentTertiary, + "iconCriticalPrimary" to iconCriticalPrimary, + "iconDisabled" to iconDisabled, + "iconInfoPrimary" to iconInfoPrimary, + "iconOnSolidPrimary" to iconOnSolidPrimary, + "iconPrimary" to iconPrimary, + "iconPrimaryAlpha" to iconPrimaryAlpha, + "iconQuaternary" to iconQuaternary, + "iconQuaternaryAlpha" to iconQuaternaryAlpha, + "iconSecondary" to iconSecondary, + "iconSecondaryAlpha" to iconSecondaryAlpha, + "iconSuccessPrimary" to iconSuccessPrimary, + "iconTertiary" to iconTertiary, + "iconTertiaryAlpha" to iconTertiaryAlpha, + "textActionAccent" to textActionAccent, + "textActionPrimary" to textActionPrimary, + "textBadgeAccent" to textBadgeAccent, + "textBadgeInfo" to textBadgeInfo, + "textCriticalPrimary" to textCriticalPrimary, + "textDecorative1" to textDecorative1, + "textDecorative2" to textDecorative2, + "textDecorative3" to textDecorative3, + "textDecorative4" to textDecorative4, + "textDecorative5" to textDecorative5, + "textDecorative6" to textDecorative6, + "textDisabled" to textDisabled, + "textInfoPrimary" to textInfoPrimary, + "textLinkExternal" to textLinkExternal, + "textOnSolidPrimary" to textOnSolidPrimary, + "textPrimary" to textPrimary, + "textSecondary" to textSecondary, + "textSuccessPrimary" to textSuccessPrimary, + "isLight" to if (isLight) Color.White else Color.Black, + ) + } +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/previews/Typography.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/previews/Typography.kt new file mode 100644 index 0000000000..5d5f31f203 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/previews/Typography.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2025 New Vector 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.compound.previews + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme + +@Preview +@Composable +internal fun TypographyPreview() = ElementTheme { + Surface { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + with(ElementTheme.materialTypography) { + TypographyTokenPreview(displayLarge, "Display large") + TypographyTokenPreview(displayMedium, "Display medium") + TypographyTokenPreview(displaySmall, "Display small") + TypographyTokenPreview(headlineLarge, "Headline large") + TypographyTokenPreview(headlineMedium, "Headline medium") + TypographyTokenPreview(headlineSmall, "Headline small") + TypographyTokenPreview(titleLarge, "Title large") + TypographyTokenPreview(titleMedium, "Title medium") + TypographyTokenPreview(titleSmall, "Title small") + TypographyTokenPreview(bodyLarge, "Body large") + TypographyTokenPreview(bodyMedium, "Body medium") + TypographyTokenPreview(bodySmall, "Body small") + TypographyTokenPreview(labelLarge, "Label large") + TypographyTokenPreview(labelMedium, "Label medium") + TypographyTokenPreview(labelSmall, "Label small") + } + } + } +} + +@Composable +private fun TypographyTokenPreview(style: TextStyle, text: String) { + Text(text = text, style = style) +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/showkase/CompoundShowkaseRootModule.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/showkase/CompoundShowkaseRootModule.kt new file mode 100644 index 0000000000..f1c7b3f8d7 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/showkase/CompoundShowkaseRootModule.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2025 New Vector 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.compound.showkase + +import com.airbnb.android.showkase.annotation.ShowkaseRoot +import com.airbnb.android.showkase.annotation.ShowkaseRootModule + +@ShowkaseRoot +class CompoundShowkaseRootModule : ShowkaseRootModule diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/theme/AvatarColors.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/AvatarColors.kt new file mode 100644 index 0000000000..506e1c8063 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/AvatarColors.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2023, 2025 New Vector 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.compound.theme + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList + +/** + * Data class to hold avatar colors. + */ +data class AvatarColors( + /** Background color for the avatar. */ + val background: Color, + /** Foreground color for the avatar. */ + val foreground: Color, +) + +/** + * Avatar colors using semantic tokens. + */ +@Composable +fun avatarColors(): List { + return listOf( + AvatarColors(background = ElementTheme.colors.bgDecorative1, foreground = ElementTheme.colors.textDecorative1), + AvatarColors(background = ElementTheme.colors.bgDecorative2, foreground = ElementTheme.colors.textDecorative2), + AvatarColors(background = ElementTheme.colors.bgDecorative3, foreground = ElementTheme.colors.textDecorative3), + AvatarColors(background = ElementTheme.colors.bgDecorative4, foreground = ElementTheme.colors.textDecorative4), + AvatarColors(background = ElementTheme.colors.bgDecorative5, foreground = ElementTheme.colors.textDecorative5), + AvatarColors(background = ElementTheme.colors.bgDecorative6, foreground = ElementTheme.colors.textDecorative6), + ) +} + +@Preview +@Composable +internal fun AvatarColorsPreviewLight() { + ElementTheme { + val chunks = avatarColors().chunked(4) + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + for (chunk in chunks) { + AvatarColorRow(chunk.toImmutableList()) + } + } + } +} + +@Preview +@Composable +internal fun AvatarColorsPreviewDark() { + ElementTheme(darkTheme = true) { + val chunks = avatarColors().chunked(4) + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + for (chunk in chunks) { + AvatarColorRow(chunk.toImmutableList()) + } + } + } +} + +@Composable +private fun AvatarColorRow(colors: ImmutableList) { + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + colors.forEach { color -> + Box( + modifier = Modifier.size(48.dp) + .background(color.background), + ) { + Text( + modifier = Modifier.align(Alignment.Center), + text = "A", + color = color.foreground, + ) + } + } + } +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/theme/ElementTheme.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/ElementTheme.kt new file mode 100644 index 0000000000..7535de0acd --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/ElementTheme.kt @@ -0,0 +1,167 @@ +/* + * Copyright 2023, 2024 New Vector 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.compound.theme + +import android.os.Build +import androidx.activity.ComponentActivity +import androidx.activity.SystemBarStyle +import androidx.activity.compose.LocalActivity +import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.ColorScheme +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Typography +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import io.element.android.compound.tokens.compoundTypography +import io.element.android.compound.tokens.generated.SemanticColors +import io.element.android.compound.tokens.generated.TypographyTokens +import io.element.android.compound.tokens.generated.compoundColorsDark +import io.element.android.compound.tokens.generated.compoundColorsLight + +/** + * Inspired from https://medium.com/@lucasyujideveloper/54cbcbde1ace + */ +object ElementTheme { + /** + * The current [SemanticColors] provided by [ElementTheme]. + * These come from Compound and are the recommended colors to use for custom components. + * In Figma, these colors usually have the `Light/` or `Dark/` prefix. + */ + val colors: SemanticColors + @Composable + @ReadOnlyComposable + get() = LocalCompoundColors.current + + /** + * The current Material 3 [ColorScheme] provided by [ElementTheme], coming from [MaterialTheme]. + * In Figma, these colors usually have the `M3/` prefix. + */ + val materialColors: ColorScheme + @Composable + @ReadOnlyComposable + get() = MaterialTheme.colorScheme + + /** + * Compound [Typography] tokens. In Figma, these have the `Android/font/` prefix. + */ + val typography: TypographyTokens = TypographyTokens + + /** + * Material 3 [Typography] tokens. In Figma, these have the `M3 Typography/` prefix. + */ + val materialTypography: Typography + @Composable + @ReadOnlyComposable + get() = MaterialTheme.typography + + /** + * Returns whether the theme version used is the light or the dark one. + */ + val isLightTheme: Boolean + @Composable + @ReadOnlyComposable + get() = LocalCompoundColors.current.isLight +} + +// Global variables (application level) +internal val LocalCompoundColors = staticCompositionLocalOf { compoundColorsLight } + +/** + * Sets up the theme for the application, or a part of it. + * + * @param darkTheme whether to use the dark theme or not. If `true`, the dark theme will be used. + * @param applySystemBarsUpdate whether to update the system bars color scheme or not when the theme changes. It's `true` by default. + * This is specially useful when you want to apply an alternate theme to a part of the app but don't want it to affect the system bars. + * @param lightStatusBar whether to use a light status bar color scheme or not. By default, it's the opposite of [darkTheme]. + * @param dynamicColor whether to enable MaterialYou or not. It's `false` by default. + * @param compoundLight the [SemanticColors] to use in light theme. + * @param compoundDark the [SemanticColors] to use in dark theme. + * @param materialColorsLight the Material 3 [ColorScheme] to use in light theme. + * @param materialColorsDark the Material 3 [ColorScheme] to use in dark theme. + * @param typography the Material 3 [Typography] tokens to use. It'll use [compoundTypography] by default. + * @param content the content to apply the theme to. + */ +@Composable +fun ElementTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + applySystemBarsUpdate: Boolean = true, + lightStatusBar: Boolean = !darkTheme, + // true to enable MaterialYou + dynamicColor: Boolean = false, + compoundLight: SemanticColors = compoundColorsLight, + compoundDark: SemanticColors = compoundColorsDark, + materialColorsLight: ColorScheme = compoundLight.toMaterialColorScheme(), + materialColorsDark: ColorScheme = compoundDark.toMaterialColorScheme(), + typography: Typography = compoundTypography, + content: @Composable () -> Unit, +) { + val currentCompoundColor = when { + darkTheme -> compoundDark + else -> compoundLight + } + + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + darkTheme -> materialColorsDark + else -> materialColorsLight + } + + val statusBarColorScheme = if (dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val context = LocalContext.current + if (lightStatusBar) { + dynamicDarkColorScheme(context) + } else { + dynamicLightColorScheme(context) + } + } else { + colorScheme + } + + if (applySystemBarsUpdate) { + val activity = LocalActivity.current as? ComponentActivity + LaunchedEffect(statusBarColorScheme, darkTheme, lightStatusBar) { + activity?.enableEdgeToEdge( + // For Status bar use the background color of the app + statusBarStyle = SystemBarStyle.auto( + lightScrim = statusBarColorScheme.background.toArgb(), + darkScrim = statusBarColorScheme.background.toArgb(), + detectDarkMode = { !lightStatusBar } + ), + // For Navigation bar use a transparent color so the content can be seen through it + navigationBarStyle = if (darkTheme) { + SystemBarStyle.dark(Color.Transparent.toArgb()) + } else { + SystemBarStyle.light(Color.Transparent.toArgb(), Color.Transparent.toArgb()) + } + ) + } + } + CompositionLocalProvider( + LocalCompoundColors provides currentCompoundColor, + LocalContentColor provides colorScheme.onSurface, + ) { + MaterialTheme( + colorScheme = colorScheme, + typography = typography, + content = content + ) + } +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/theme/ForcedDarkElementTheme.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/ForcedDarkElementTheme.kt new file mode 100644 index 0000000000..cd168713ae --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/ForcedDarkElementTheme.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2023, 2024 New Vector 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.compound.theme + +import androidx.activity.ComponentActivity +import androidx.activity.SystemBarStyle +import androidx.activity.compose.LocalActivity +import androidx.activity.enableEdgeToEdge +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb + +/** + * Can be used to force a composable in dark theme. + * It will automatically change the system ui colors back to normal when leaving the composition. + */ +@Composable +fun ForcedDarkElementTheme( + lightStatusBar: Boolean = false, + content: @Composable () -> Unit, +) { + val colorScheme = MaterialTheme.colorScheme + val wasDarkTheme = !ElementTheme.colors.isLight + val activity = LocalActivity.current as? ComponentActivity + DisposableEffect(Unit) { + onDispose { + activity?.enableEdgeToEdge( + statusBarStyle = SystemBarStyle.auto( + lightScrim = colorScheme.background.toArgb(), + darkScrim = colorScheme.background.toArgb(), + ), + navigationBarStyle = if (wasDarkTheme) { + SystemBarStyle.dark(Color.Transparent.toArgb()) + } else { + SystemBarStyle.light( + scrim = Color.Transparent.toArgb(), + darkScrim = Color.Transparent.toArgb() + ) + } + ) + } + } + ElementTheme(darkTheme = true, lightStatusBar = lightStatusBar, content = content) +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/theme/LegacyColors.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/LegacyColors.kt new file mode 100644 index 0000000000..6b7814d1e3 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/LegacyColors.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2023, 2024 New Vector 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.compound.theme + +import androidx.compose.ui.graphics.Color +import io.element.android.compound.annotations.CoreColorToken +import io.element.android.compound.tokens.generated.internal.DarkColorTokens +import io.element.android.compound.tokens.generated.internal.LightColorTokens + +// ================================================================================================= +// IMPORTANT! +// We should not be adding any new colors here. This file is only for legacy colors. +// In fact, we should try to remove any references to these colors as we +// iterate through the designs. All new colors should come from Compound's Design Tokens. +// ================================================================================================= + +val LinkColor = Color(0xFF0086E6) + +@OptIn(CoreColorToken::class) +val SnackBarLabelColorLight = LightColorTokens.colorGray700 +@OptIn(CoreColorToken::class) +val SnackBarLabelColorDark = DarkColorTokens.colorGray700 diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/theme/MaterialColorSchemeDark.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/MaterialColorSchemeDark.kt new file mode 100644 index 0000000000..7ee6b631b5 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/MaterialColorSchemeDark.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2025 New Vector 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.compound.theme + +import androidx.compose.material3.ColorScheme +import androidx.compose.material3.darkColorScheme +import io.element.android.compound.annotations.CoreColorToken +import io.element.android.compound.tokens.generated.SemanticColors +import io.element.android.compound.tokens.generated.internal.DarkColorTokens + +/** + * See the mapping in + * https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=311-14&p=f&t=QcVyNaPEZMDA6RFK-0 + */ +@OptIn(CoreColorToken::class) +fun SemanticColors.toMaterialColorSchemeDark(): ColorScheme = darkColorScheme( + primary = bgActionPrimaryRest, + onPrimary = textOnSolidPrimary, + primaryContainer = bgCanvasDefault, + onPrimaryContainer = textPrimary, + inversePrimary = textOnSolidPrimary, + secondary = textSecondary, + onSecondary = textOnSolidPrimary, + secondaryContainer = bgSubtlePrimary, + onSecondaryContainer = textPrimary, + tertiary = textSecondary, + onTertiary = textOnSolidPrimary, + tertiaryContainer = bgActionPrimaryRest, + onTertiaryContainer = textOnSolidPrimary, + background = bgCanvasDefault, + onBackground = textPrimary, + surface = bgCanvasDefault, + onSurface = textPrimary, + surfaceVariant = bgSubtleSecondary, + onSurfaceVariant = textSecondary, + surfaceTint = DarkColorTokens.colorGray1000, + inverseSurface = DarkColorTokens.colorGray1300, + inverseOnSurface = textOnSolidPrimary, + error = textCriticalPrimary, + onError = textOnSolidPrimary, + errorContainer = DarkColorTokens.colorRed400, + onErrorContainer = textCriticalPrimary, + outline = borderInteractivePrimary, + outlineVariant = DarkColorTokens.colorAlphaGray400, + // Note: for light it will be colorGray1400 + scrim = DarkColorTokens.colorGray300, +) diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/theme/MaterialColorSchemeLight.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/MaterialColorSchemeLight.kt new file mode 100644 index 0000000000..b179234429 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/MaterialColorSchemeLight.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2025 New Vector 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.compound.theme + +import androidx.compose.material3.ColorScheme +import androidx.compose.material3.lightColorScheme +import io.element.android.compound.annotations.CoreColorToken +import io.element.android.compound.tokens.generated.SemanticColors +import io.element.android.compound.tokens.generated.internal.LightColorTokens + +/** + * See the mapping in + * https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=311-14&p=f&t=QcVyNaPEZMDA6RFK-0 + */ +@OptIn(CoreColorToken::class) +fun SemanticColors.toMaterialColorSchemeLight(): ColorScheme = lightColorScheme( + primary = bgActionPrimaryRest, + onPrimary = textOnSolidPrimary, + primaryContainer = bgCanvasDefault, + onPrimaryContainer = textPrimary, + inversePrimary = textOnSolidPrimary, + secondary = textSecondary, + onSecondary = textOnSolidPrimary, + secondaryContainer = bgSubtlePrimary, + onSecondaryContainer = textPrimary, + tertiary = textSecondary, + onTertiary = textOnSolidPrimary, + tertiaryContainer = bgActionPrimaryRest, + onTertiaryContainer = textOnSolidPrimary, + background = bgCanvasDefault, + onBackground = textPrimary, + surface = bgCanvasDefault, + onSurface = textPrimary, + surfaceVariant = bgSubtleSecondary, + onSurfaceVariant = textSecondary, + surfaceTint = LightColorTokens.colorGray1000, + inverseSurface = LightColorTokens.colorGray1300, + inverseOnSurface = textOnSolidPrimary, + error = textCriticalPrimary, + onError = textOnSolidPrimary, + errorContainer = LightColorTokens.colorRed400, + onErrorContainer = textCriticalPrimary, + outline = borderInteractivePrimary, + outlineVariant = LightColorTokens.colorAlphaGray400, + // Note: for dark it will be colorGray300 + scrim = LightColorTokens.colorGray1400, +) diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/theme/MaterialTextPreview.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/MaterialTextPreview.kt new file mode 100644 index 0000000000..724d440161 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/MaterialTextPreview.kt @@ -0,0 +1,138 @@ +/* + * Copyright 2025 New Vector 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.compound.theme + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.element.android.compound.utils.toHrf + +@Preview(heightDp = 1200, widthDp = 420) +@Composable +internal fun MaterialTextPreview() = Row( + modifier = Modifier.background(Color.Yellow) +) { + MaterialPreview( + modifier = Modifier.weight(1f), + darkTheme = false, + ) + MaterialPreview( + modifier = Modifier.weight(1f), + darkTheme = true, + ) +} + +private data class Model( + val name: String, + val bgColor: Color, + val textColor: Color, +) + +@Composable +private fun MaterialPreview( + darkTheme: Boolean, + modifier: Modifier = Modifier, +) = Column(modifier = modifier) { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + textAlign = TextAlign.Center, + text = if (darkTheme) "Dark" else "Light", + color = Color.Black, + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + ) + ElementTheme( + darkTheme = darkTheme, + ) { + Column( + modifier = Modifier.fillMaxSize() + ) { + listOf( + Model("Background", MaterialTheme.colorScheme.background, MaterialTheme.colorScheme.onBackground), + Model("Primary", MaterialTheme.colorScheme.primary, MaterialTheme.colorScheme.onPrimary), + Model("PrimaryContainer", MaterialTheme.colorScheme.primaryContainer, MaterialTheme.colorScheme.onPrimaryContainer), + Model("Secondary", MaterialTheme.colorScheme.secondary, MaterialTheme.colorScheme.onSecondary), + Model("SecondaryContainer", MaterialTheme.colorScheme.secondaryContainer, MaterialTheme.colorScheme.onSecondaryContainer), + Model("Tertiary", MaterialTheme.colorScheme.tertiary, MaterialTheme.colorScheme.onTertiary), + Model("TertiaryContainer", MaterialTheme.colorScheme.tertiaryContainer, MaterialTheme.colorScheme.onTertiaryContainer), + Model("Surface", MaterialTheme.colorScheme.surface, MaterialTheme.colorScheme.onSurface), + Model("SurfaceVariant", MaterialTheme.colorScheme.surfaceVariant, MaterialTheme.colorScheme.onSurfaceVariant), + Model("InverseSurface", MaterialTheme.colorScheme.inverseSurface, MaterialTheme.colorScheme.inverseOnSurface), + Model("Error", MaterialTheme.colorScheme.error, MaterialTheme.colorScheme.onError), + Model("ErrorContainer", MaterialTheme.colorScheme.errorContainer, MaterialTheme.colorScheme.onErrorContainer), + ).forEach { + TextPreview( + name = it.name, + bgColor = it.bgColor, + textColor = it.textColor, + ) + } + Box( + modifier = Modifier + .padding(1.dp) + .fillMaxWidth() + .background(MaterialTheme.colorScheme.background) + ) { + Text( + text = "Below\n".repeat(3), + color = MaterialTheme.colorScheme.onBackground, + ) + Text( + modifier = Modifier + .padding(12.dp) + .fillMaxWidth() + // the alpha applied to the scrim color does not seem to be mandatory. + // The library ignores the alpha level provided and apply it's own. + // For testing the color, manually set an alpha. + .background(color = MaterialTheme.colorScheme.scrim.copy(alpha = 0.32f)) + .padding(16.dp), + text = "${"Scrim"}\n${MaterialTheme.colorScheme.scrim.toHrf()}", + style = LocalTextStyle.current.copy(fontFamily = FontFamily.Monospace), + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onBackground, + ) + } + } + } +} + +@Composable +private fun TextPreview( + name: String, + bgColor: Color, + textColor: Color, + modifier: Modifier = Modifier, +) = Text( + modifier = modifier + .padding(1.dp) + .fillMaxWidth() + .background(bgColor) + .padding(horizontal = 16.dp, vertical = 8.dp), + text = "$name\n${textColor.toHrf()}\n${bgColor.toHrf()}", + style = LocalTextStyle.current.copy(fontFamily = FontFamily.Monospace), + textAlign = TextAlign.Center, + color = textColor, +) diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/theme/MaterialThemeColors.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/MaterialThemeColors.kt new file mode 100644 index 0000000000..f81b0a94f6 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/MaterialThemeColors.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2023, 2024 New Vector 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.compound.theme + +import androidx.compose.material3.ColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import io.element.android.compound.previews.ColorsSchemePreview +import io.element.android.compound.tokens.generated.SemanticColors +import io.element.android.compound.tokens.generated.compoundColorsHcDark +import io.element.android.compound.tokens.generated.compoundColorsHcLight + +fun SemanticColors.toMaterialColorScheme(): ColorScheme { + return if (isLight) { + toMaterialColorSchemeLight() + } else { + toMaterialColorSchemeDark() + } +} + +@Preview(heightDp = 1200) +@Composable +internal fun ColorsSchemeLightPreview() = ElementTheme { + ColorsSchemePreview( + Color.Black, + Color.White, + ElementTheme.materialColors, + ) +} + +@Preview(heightDp = 1200) +@Composable +internal fun ColorsSchemeLightHcPreview() = ElementTheme( + compoundLight = compoundColorsHcLight, +) { + ColorsSchemePreview( + Color.Black, + Color.White, + ElementTheme.materialColors, + ) +} + +@Preview(heightDp = 1200) +@Composable +internal fun ColorsSchemeDarkPreview() = ElementTheme( + darkTheme = true, +) { + ColorsSchemePreview( + Color.White, + Color.Black, + ElementTheme.materialColors, + ) +} + +@Preview(heightDp = 1200) +@Composable +internal fun ColorsSchemeDarkHcPreview() = ElementTheme( + darkTheme = true, + compoundDark = compoundColorsHcDark, +) { + ColorsSchemePreview( + Color.White, + Color.Black, + ElementTheme.materialColors, + ) +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/theme/Theme.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/Theme.kt new file mode 100644 index 0000000000..06668e2952 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/theme/Theme.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2023, 2024 New Vector 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.compound.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.Composable +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +enum class Theme { + System, + Dark, + Light, +} + +@Composable +fun Theme.isDark(): Boolean { + return when (this) { + Theme.System -> isSystemInDarkTheme() + Theme.Dark -> true + Theme.Light -> false + } +} + +fun Flow.mapToTheme(): Flow = map { + when (it) { + null -> Theme.System + else -> Theme.valueOf(it) + } +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/CompoundTypography.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/CompoundTypography.kt new file mode 100644 index 0000000000..ab4d898ba4 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/CompoundTypography.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2023, 2024 New Vector 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.compound.tokens + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.PlatformTextStyle +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.LineHeightStyle +import androidx.compose.ui.unit.em +import androidx.compose.ui.unit.sp +import com.airbnb.android.showkase.annotation.ShowkaseTypography +import io.element.android.compound.tokens.generated.TypographyTokens + +// 32px (Material) vs 34px, it's the closest one +@ShowkaseTypography(name = "M3 Headline Large", group = "Compound") +internal val compoundHeadingXlRegular = TypographyTokens.fontHeadingXlRegular + +// both are 28px +@ShowkaseTypography(name = "M3 Headline Medium", group = "Compound") +internal val compoundHeadingLgRegular = TypographyTokens.fontHeadingLgRegular + +// These are the default M3 values, but we're setting them manually so an update in M3 doesn't break our designs +@ShowkaseTypography(name = "M3 Headline Small", group = "Compound") +internal val defaultHeadlineSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + lineHeight = 32.sp, + fontSize = 24.sp, + letterSpacing = 0.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) +) + +// 22px (Material) vs 20px, it's the closest one +@ShowkaseTypography(name = "M3 Title Large", group = "Compound") +internal val compoundHeadingMdRegular = TypographyTokens.fontHeadingMdRegular + +// 16px both +@ShowkaseTypography(name = "M3 Title Medium", group = "Compound") +internal val compoundBodyLgMedium = TypographyTokens.fontBodyLgMedium + +// 14px both +@ShowkaseTypography(name = "M3 Title Small", group = "Compound") +internal val compoundBodyMdMedium = TypographyTokens.fontBodyMdMedium + +// 16px both +@ShowkaseTypography(name = "M3 Body Large", group = "Compound") +internal val compoundBodyLgRegular = TypographyTokens.fontBodyLgRegular + +// 14px both +@ShowkaseTypography(name = "M3 Body Medium", group = "Compound") +internal val compoundBodyMdRegular = TypographyTokens.fontBodyMdRegular + +// 12px both +@ShowkaseTypography(name = "M3 Body Small", group = "Compound") +internal val compoundBodySmRegular = TypographyTokens.fontBodySmRegular + +// 14px both, Title Small uses the same token so we have to declare it twice +@ShowkaseTypography(name = "M3 Label Large", group = "Compound") +internal val compoundBodyMdMedium_LabelLarge = TypographyTokens.fontBodyMdMedium + +// 12px both +@ShowkaseTypography(name = "M3 Label Medium", group = "Compound") +internal val compoundBodySmMedium = TypographyTokens.fontBodySmMedium + +// 11px both +@ShowkaseTypography(name = "M3 Label Small", group = "Compound") +internal val compoundBodyXsMedium = TypographyTokens.fontBodyXsMedium + +internal val compoundTypography = Typography( + // displayLarge = , 57px (Material) size. We have no equivalent + // displayMedium = , 45px (Material) size. We have no equivalent + // displaySmall = , 36px (Material) size. We have no equivalent + headlineLarge = compoundHeadingXlRegular, + headlineMedium = compoundHeadingLgRegular, + headlineSmall = defaultHeadlineSmall, + titleLarge = compoundHeadingMdRegular, + titleMedium = compoundBodyLgMedium, + titleSmall = compoundBodyMdMedium, + bodyLarge = compoundBodyLgRegular, + bodyMedium = compoundBodyMdRegular, + bodySmall = compoundBodySmRegular, + labelLarge = compoundBodyMdMedium_LabelLarge, + labelMedium = compoundBodySmMedium, + labelSmall = compoundBodyXsMedium, +) diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/CompoundIcons.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/CompoundIcons.kt new file mode 100644 index 0000000000..76b3275651 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/CompoundIcons.kt @@ -0,0 +1,1034 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + + +/** + * !!! WARNING !!! + * + * THIS IS AN AUTOGENERATED FILE. + * DO NOT EDIT MANUALLY. + */ + + + +@file:Suppress("all") +package io.element.android.compound.tokens.generated + +import io.element.android.compound.R +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import kotlinx.collections.immutable.persistentListOf + +object CompoundIcons { + @Composable fun Admin(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_admin) + } + @Composable fun ArrowDown(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_arrow_down) + } + @Composable fun ArrowLeft(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_arrow_left) + } + @Composable fun ArrowRight(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_arrow_right) + } + @Composable fun ArrowUp(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_arrow_up) + } + @Composable fun ArrowUpRight(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_arrow_up_right) + } + @Composable fun AskToJoin(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_ask_to_join) + } + @Composable fun AskToJoinSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_ask_to_join_solid) + } + @Composable fun Attachment(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_attachment) + } + @Composable fun Audio(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_audio) + } + @Composable fun Block(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_block) + } + @Composable fun Bold(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_bold) + } + @Composable fun Calendar(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_calendar) + } + @Composable fun Chart(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_chart) + } + @Composable fun Chat(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_chat) + } + @Composable fun ChatNew(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_chat_new) + } + @Composable fun ChatProblem(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_chat_problem) + } + @Composable fun ChatSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_chat_solid) + } + @Composable fun Check(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_check) + } + @Composable fun CheckCircle(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_check_circle) + } + @Composable fun CheckCircleSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_check_circle_solid) + } + @Composable fun ChevronDown(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_chevron_down) + } + @Composable fun ChevronLeft(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_chevron_left) + } + @Composable fun ChevronRight(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_chevron_right) + } + @Composable fun ChevronUp(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_chevron_up) + } + @Composable fun ChevronUpDown(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_chevron_up_down) + } + @Composable fun Circle(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_circle) + } + @Composable fun Close(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_close) + } + @Composable fun Cloud(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_cloud) + } + @Composable fun CloudSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_cloud_solid) + } + @Composable fun Code(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_code) + } + @Composable fun Collapse(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_collapse) + } + @Composable fun Company(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_company) + } + @Composable fun Compose(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_compose) + } + @Composable fun Computer(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_computer) + } + @Composable fun Copy(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_copy) + } + @Composable fun DarkMode(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_dark_mode) + } + @Composable fun Delete(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_delete) + } + @Composable fun Devices(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_devices) + } + @Composable fun DialPad(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_dial_pad) + } + @Composable fun Document(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_document) + } + @Composable fun Download(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_download) + } + @Composable fun DownloadIos(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_download_ios) + } + @Composable fun DragGrid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_drag_grid) + } + @Composable fun DragList(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_drag_list) + } + @Composable fun Earpiece(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_earpiece) + } + @Composable fun Edit(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_edit) + } + @Composable fun EditSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_edit_solid) + } + @Composable fun Email(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_email) + } + @Composable fun EmailSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_email_solid) + } + @Composable fun EndCall(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_end_call) + } + @Composable fun Error(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_error) + } + @Composable fun ErrorSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_error_solid) + } + @Composable fun Expand(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_expand) + } + @Composable fun Explore(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_explore) + } + @Composable fun ExportArchive(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_export_archive) + } + @Composable fun Extensions(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_extensions) + } + @Composable fun ExtensionsSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_extensions_solid) + } + @Composable fun Favourite(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_favourite) + } + @Composable fun FavouriteSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_favourite_solid) + } + @Composable fun FileError(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_file_error) + } + @Composable fun Files(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_files) + } + @Composable fun Filter(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_filter) + } + @Composable fun Forward(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_forward) + } + @Composable fun Grid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_grid) + } + @Composable fun Group(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_group) + } + @Composable fun Guest(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_guest) + } + @Composable fun HeadphonesOffSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_headphones_off_solid) + } + @Composable fun HeadphonesSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_headphones_solid) + } + @Composable fun Help(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_help) + } + @Composable fun HelpSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_help_solid) + } + @Composable fun History(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_history) + } + @Composable fun Home(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_home) + } + @Composable fun HomeSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_home_solid) + } + @Composable fun Host(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_host) + } + @Composable fun Image(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_image) + } + @Composable fun ImageError(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_image_error) + } + @Composable fun IndentDecrease(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_indent_decrease) + } + @Composable fun IndentIncrease(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_indent_increase) + } + @Composable fun Info(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_info) + } + @Composable fun InfoSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_info_solid) + } + @Composable fun InlineCode(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_inline_code) + } + @Composable fun Italic(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_italic) + } + @Composable fun Key(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_key) + } + @Composable fun KeyOff(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_key_off) + } + @Composable fun KeyOffSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_key_off_solid) + } + @Composable fun KeySolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_key_solid) + } + @Composable fun Keyboard(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_keyboard) + } + @Composable fun Labs(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_labs) + } + @Composable fun Leave(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_leave) + } + @Composable fun Link(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_link) + } + @Composable fun Linux(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_linux) + } + @Composable fun ListBulleted(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_list_bulleted) + } + @Composable fun ListNumbered(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_list_numbered) + } + @Composable fun ListView(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_list_view) + } + @Composable fun LocationNavigator(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_location_navigator) + } + @Composable fun LocationNavigatorCentred(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_location_navigator_centred) + } + @Composable fun LocationPin(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_location_pin) + } + @Composable fun LocationPinSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_location_pin_solid) + } + @Composable fun Lock(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_lock) + } + @Composable fun LockOff(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_lock_off) + } + @Composable fun LockSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_lock_solid) + } + @Composable fun Mac(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_mac) + } + @Composable fun MarkAsRead(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_mark_as_read) + } + @Composable fun MarkAsUnread(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_mark_as_unread) + } + @Composable fun MarkThreadsAsRead(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_mark_threads_as_read) + } + @Composable fun MarkerReadReceipts(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_marker_read_receipts) + } + @Composable fun Mention(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_mention) + } + @Composable fun Menu(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_menu) + } + @Composable fun MicOff(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_mic_off) + } + @Composable fun MicOffSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_mic_off_solid) + } + @Composable fun MicOn(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_mic_on) + } + @Composable fun MicOnSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_mic_on_solid) + } + @Composable fun Minus(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_minus) + } + @Composable fun Mobile(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_mobile) + } + @Composable fun Notifications(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_notifications) + } + @Composable fun NotificationsOff(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_notifications_off) + } + @Composable fun NotificationsOffSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_notifications_off_solid) + } + @Composable fun NotificationsSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_notifications_solid) + } + @Composable fun Offline(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_offline) + } + @Composable fun OverflowHorizontal(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_overflow_horizontal) + } + @Composable fun OverflowVertical(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_overflow_vertical) + } + @Composable fun Pause(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_pause) + } + @Composable fun PauseSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_pause_solid) + } + @Composable fun Pin(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_pin) + } + @Composable fun PinSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_pin_solid) + } + @Composable fun Play(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_play) + } + @Composable fun PlaySolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_play_solid) + } + @Composable fun Plus(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_plus) + } + @Composable fun Polls(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_polls) + } + @Composable fun PollsEnd(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_polls_end) + } + @Composable fun PopOut(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_pop_out) + } + @Composable fun Preferences(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_preferences) + } + @Composable fun PresenceOutline8X8(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_presence_outline_8_x_8) + } + @Composable fun PresenceSolid8X8(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_presence_solid_8_x_8) + } + @Composable fun PresenceStrikethrough8X8(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_presence_strikethrough_8_x_8) + } + @Composable fun Public(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_public) + } + @Composable fun QrCode(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_qr_code) + } + @Composable fun Quote(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_quote) + } + @Composable fun RaisedHandSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_raised_hand_solid) + } + @Composable fun Reaction(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_reaction) + } + @Composable fun ReactionAdd(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_reaction_add) + } + @Composable fun ReactionSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_reaction_solid) + } + @Composable fun Reply(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_reply) + } + @Composable fun Restart(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_restart) + } + @Composable fun Room(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_room) + } + @Composable fun Search(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_search) + } + @Composable fun Send(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_send) + } + @Composable fun SendSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_send_solid) + } + @Composable fun Settings(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_settings) + } + @Composable fun SettingsSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_settings_solid) + } + @Composable fun Share(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_share) + } + @Composable fun ShareAndroid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_share_android) + } + @Composable fun ShareIos(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_share_ios) + } + @Composable fun ShareScreen(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_share_screen) + } + @Composable fun ShareScreenSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_share_screen_solid) + } + @Composable fun Shield(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_shield) + } + @Composable fun Sidebar(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_sidebar) + } + @Composable fun SignOut(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_sign_out) + } + @Composable fun Spinner(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_spinner) + } + @Composable fun Spotlight(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_spotlight) + } + @Composable fun SpotlightView(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_spotlight_view) + } + @Composable fun Strikethrough(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_strikethrough) + } + @Composable fun SwitchCameraSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_switch_camera_solid) + } + @Composable fun TakePhoto(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_take_photo) + } + @Composable fun TakePhotoSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_take_photo_solid) + } + @Composable fun TextFormatting(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_text_formatting) + } + @Composable fun Threads(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_threads) + } + @Composable fun ThreadsSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_threads_solid) + } + @Composable fun Time(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_time) + } + @Composable fun Underline(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_underline) + } + @Composable fun Unknown(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_unknown) + } + @Composable fun UnknownSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_unknown_solid) + } + @Composable fun Unpin(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_unpin) + } + @Composable fun User(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_user) + } + @Composable fun UserAdd(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_user_add) + } + @Composable fun UserAddSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_user_add_solid) + } + @Composable fun UserProfile(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_user_profile) + } + @Composable fun UserProfileSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_user_profile_solid) + } + @Composable fun UserSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_user_solid) + } + @Composable fun Verified(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_verified) + } + @Composable fun VideoCall(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_video_call) + } + @Composable fun VideoCallDeclinedSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_video_call_declined_solid) + } + @Composable fun VideoCallMissedSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_video_call_missed_solid) + } + @Composable fun VideoCallOff(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_video_call_off) + } + @Composable fun VideoCallOffSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_video_call_off_solid) + } + @Composable fun VideoCallSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_video_call_solid) + } + @Composable fun VisibilityOff(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_visibility_off) + } + @Composable fun VisibilityOn(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_visibility_on) + } + @Composable fun VoiceCall(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_voice_call) + } + @Composable fun VoiceCallSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_voice_call_solid) + } + @Composable fun VolumeOff(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_volume_off) + } + @Composable fun VolumeOffSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_volume_off_solid) + } + @Composable fun VolumeOn(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_volume_on) + } + @Composable fun VolumeOnSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_volume_on_solid) + } + @Composable fun Warning(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_warning) + } + @Composable fun WebBrowser(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_web_browser) + } + @Composable fun Windows(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_windows) + } + @Composable fun Workspace(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_workspace) + } + @Composable fun WorkspaceSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_workspace_solid) + } + + val all @Composable get() = persistentListOf( + Admin(), + ArrowDown(), + ArrowLeft(), + ArrowRight(), + ArrowUp(), + ArrowUpRight(), + AskToJoin(), + AskToJoinSolid(), + Attachment(), + Audio(), + Block(), + Bold(), + Calendar(), + Chart(), + Chat(), + ChatNew(), + ChatProblem(), + ChatSolid(), + Check(), + CheckCircle(), + CheckCircleSolid(), + ChevronDown(), + ChevronLeft(), + ChevronRight(), + ChevronUp(), + ChevronUpDown(), + Circle(), + Close(), + Cloud(), + CloudSolid(), + Code(), + Collapse(), + Company(), + Compose(), + Computer(), + Copy(), + DarkMode(), + Delete(), + Devices(), + DialPad(), + Document(), + Download(), + DownloadIos(), + DragGrid(), + DragList(), + Earpiece(), + Edit(), + EditSolid(), + Email(), + EmailSolid(), + EndCall(), + Error(), + ErrorSolid(), + Expand(), + Explore(), + ExportArchive(), + Extensions(), + ExtensionsSolid(), + Favourite(), + FavouriteSolid(), + FileError(), + Files(), + Filter(), + Forward(), + Grid(), + Group(), + Guest(), + HeadphonesOffSolid(), + HeadphonesSolid(), + Help(), + HelpSolid(), + History(), + Home(), + HomeSolid(), + Host(), + Image(), + ImageError(), + IndentDecrease(), + IndentIncrease(), + Info(), + InfoSolid(), + InlineCode(), + Italic(), + Key(), + KeyOff(), + KeyOffSolid(), + KeySolid(), + Keyboard(), + Labs(), + Leave(), + Link(), + Linux(), + ListBulleted(), + ListNumbered(), + ListView(), + LocationNavigator(), + LocationNavigatorCentred(), + LocationPin(), + LocationPinSolid(), + Lock(), + LockOff(), + LockSolid(), + Mac(), + MarkAsRead(), + MarkAsUnread(), + MarkThreadsAsRead(), + MarkerReadReceipts(), + Mention(), + Menu(), + MicOff(), + MicOffSolid(), + MicOn(), + MicOnSolid(), + Minus(), + Mobile(), + Notifications(), + NotificationsOff(), + NotificationsOffSolid(), + NotificationsSolid(), + Offline(), + OverflowHorizontal(), + OverflowVertical(), + Pause(), + PauseSolid(), + Pin(), + PinSolid(), + Play(), + PlaySolid(), + Plus(), + Polls(), + PollsEnd(), + PopOut(), + Preferences(), + PresenceOutline8X8(), + PresenceSolid8X8(), + PresenceStrikethrough8X8(), + Public(), + QrCode(), + Quote(), + RaisedHandSolid(), + Reaction(), + ReactionAdd(), + ReactionSolid(), + Reply(), + Restart(), + Room(), + Search(), + Send(), + SendSolid(), + Settings(), + SettingsSolid(), + Share(), + ShareAndroid(), + ShareIos(), + ShareScreen(), + ShareScreenSolid(), + Shield(), + Sidebar(), + SignOut(), + Spinner(), + Spotlight(), + SpotlightView(), + Strikethrough(), + SwitchCameraSolid(), + TakePhoto(), + TakePhotoSolid(), + TextFormatting(), + Threads(), + ThreadsSolid(), + Time(), + Underline(), + Unknown(), + UnknownSolid(), + Unpin(), + User(), + UserAdd(), + UserAddSolid(), + UserProfile(), + UserProfileSolid(), + UserSolid(), + Verified(), + VideoCall(), + VideoCallDeclinedSolid(), + VideoCallMissedSolid(), + VideoCallOff(), + VideoCallOffSolid(), + VideoCallSolid(), + VisibilityOff(), + VisibilityOn(), + VoiceCall(), + VoiceCallSolid(), + VolumeOff(), + VolumeOffSolid(), + VolumeOn(), + VolumeOnSolid(), + Warning(), + WebBrowser(), + Windows(), + Workspace(), + WorkspaceSolid(), + ) + + val allResIds get() = persistentListOf( + R.drawable.ic_compound_admin, + R.drawable.ic_compound_arrow_down, + R.drawable.ic_compound_arrow_left, + R.drawable.ic_compound_arrow_right, + R.drawable.ic_compound_arrow_up, + R.drawable.ic_compound_arrow_up_right, + R.drawable.ic_compound_ask_to_join, + R.drawable.ic_compound_ask_to_join_solid, + R.drawable.ic_compound_attachment, + R.drawable.ic_compound_audio, + R.drawable.ic_compound_block, + R.drawable.ic_compound_bold, + R.drawable.ic_compound_calendar, + R.drawable.ic_compound_chart, + R.drawable.ic_compound_chat, + R.drawable.ic_compound_chat_new, + R.drawable.ic_compound_chat_problem, + R.drawable.ic_compound_chat_solid, + R.drawable.ic_compound_check, + R.drawable.ic_compound_check_circle, + R.drawable.ic_compound_check_circle_solid, + R.drawable.ic_compound_chevron_down, + R.drawable.ic_compound_chevron_left, + R.drawable.ic_compound_chevron_right, + R.drawable.ic_compound_chevron_up, + R.drawable.ic_compound_chevron_up_down, + R.drawable.ic_compound_circle, + R.drawable.ic_compound_close, + R.drawable.ic_compound_cloud, + R.drawable.ic_compound_cloud_solid, + R.drawable.ic_compound_code, + R.drawable.ic_compound_collapse, + R.drawable.ic_compound_company, + R.drawable.ic_compound_compose, + R.drawable.ic_compound_computer, + R.drawable.ic_compound_copy, + R.drawable.ic_compound_dark_mode, + R.drawable.ic_compound_delete, + R.drawable.ic_compound_devices, + R.drawable.ic_compound_dial_pad, + R.drawable.ic_compound_document, + R.drawable.ic_compound_download, + R.drawable.ic_compound_download_ios, + R.drawable.ic_compound_drag_grid, + R.drawable.ic_compound_drag_list, + R.drawable.ic_compound_earpiece, + R.drawable.ic_compound_edit, + R.drawable.ic_compound_edit_solid, + R.drawable.ic_compound_email, + R.drawable.ic_compound_email_solid, + R.drawable.ic_compound_end_call, + R.drawable.ic_compound_error, + R.drawable.ic_compound_error_solid, + R.drawable.ic_compound_expand, + R.drawable.ic_compound_explore, + R.drawable.ic_compound_export_archive, + R.drawable.ic_compound_extensions, + R.drawable.ic_compound_extensions_solid, + R.drawable.ic_compound_favourite, + R.drawable.ic_compound_favourite_solid, + R.drawable.ic_compound_file_error, + R.drawable.ic_compound_files, + R.drawable.ic_compound_filter, + R.drawable.ic_compound_forward, + R.drawable.ic_compound_grid, + R.drawable.ic_compound_group, + R.drawable.ic_compound_guest, + R.drawable.ic_compound_headphones_off_solid, + R.drawable.ic_compound_headphones_solid, + R.drawable.ic_compound_help, + R.drawable.ic_compound_help_solid, + R.drawable.ic_compound_history, + R.drawable.ic_compound_home, + R.drawable.ic_compound_home_solid, + R.drawable.ic_compound_host, + R.drawable.ic_compound_image, + R.drawable.ic_compound_image_error, + R.drawable.ic_compound_indent_decrease, + R.drawable.ic_compound_indent_increase, + R.drawable.ic_compound_info, + R.drawable.ic_compound_info_solid, + R.drawable.ic_compound_inline_code, + R.drawable.ic_compound_italic, + R.drawable.ic_compound_key, + R.drawable.ic_compound_key_off, + R.drawable.ic_compound_key_off_solid, + R.drawable.ic_compound_key_solid, + R.drawable.ic_compound_keyboard, + R.drawable.ic_compound_labs, + R.drawable.ic_compound_leave, + R.drawable.ic_compound_link, + R.drawable.ic_compound_linux, + R.drawable.ic_compound_list_bulleted, + R.drawable.ic_compound_list_numbered, + R.drawable.ic_compound_list_view, + R.drawable.ic_compound_location_navigator, + R.drawable.ic_compound_location_navigator_centred, + R.drawable.ic_compound_location_pin, + R.drawable.ic_compound_location_pin_solid, + R.drawable.ic_compound_lock, + R.drawable.ic_compound_lock_off, + R.drawable.ic_compound_lock_solid, + R.drawable.ic_compound_mac, + R.drawable.ic_compound_mark_as_read, + R.drawable.ic_compound_mark_as_unread, + R.drawable.ic_compound_mark_threads_as_read, + R.drawable.ic_compound_marker_read_receipts, + R.drawable.ic_compound_mention, + R.drawable.ic_compound_menu, + R.drawable.ic_compound_mic_off, + R.drawable.ic_compound_mic_off_solid, + R.drawable.ic_compound_mic_on, + R.drawable.ic_compound_mic_on_solid, + R.drawable.ic_compound_minus, + R.drawable.ic_compound_mobile, + R.drawable.ic_compound_notifications, + R.drawable.ic_compound_notifications_off, + R.drawable.ic_compound_notifications_off_solid, + R.drawable.ic_compound_notifications_solid, + R.drawable.ic_compound_offline, + R.drawable.ic_compound_overflow_horizontal, + R.drawable.ic_compound_overflow_vertical, + R.drawable.ic_compound_pause, + R.drawable.ic_compound_pause_solid, + R.drawable.ic_compound_pin, + R.drawable.ic_compound_pin_solid, + R.drawable.ic_compound_play, + R.drawable.ic_compound_play_solid, + R.drawable.ic_compound_plus, + R.drawable.ic_compound_polls, + R.drawable.ic_compound_polls_end, + R.drawable.ic_compound_pop_out, + R.drawable.ic_compound_preferences, + R.drawable.ic_compound_presence_outline_8_x_8, + R.drawable.ic_compound_presence_solid_8_x_8, + R.drawable.ic_compound_presence_strikethrough_8_x_8, + R.drawable.ic_compound_public, + R.drawable.ic_compound_qr_code, + R.drawable.ic_compound_quote, + R.drawable.ic_compound_raised_hand_solid, + R.drawable.ic_compound_reaction, + R.drawable.ic_compound_reaction_add, + R.drawable.ic_compound_reaction_solid, + R.drawable.ic_compound_reply, + R.drawable.ic_compound_restart, + R.drawable.ic_compound_room, + R.drawable.ic_compound_search, + R.drawable.ic_compound_send, + R.drawable.ic_compound_send_solid, + R.drawable.ic_compound_settings, + R.drawable.ic_compound_settings_solid, + R.drawable.ic_compound_share, + R.drawable.ic_compound_share_android, + R.drawable.ic_compound_share_ios, + R.drawable.ic_compound_share_screen, + R.drawable.ic_compound_share_screen_solid, + R.drawable.ic_compound_shield, + R.drawable.ic_compound_sidebar, + R.drawable.ic_compound_sign_out, + R.drawable.ic_compound_spinner, + R.drawable.ic_compound_spotlight, + R.drawable.ic_compound_spotlight_view, + R.drawable.ic_compound_strikethrough, + R.drawable.ic_compound_switch_camera_solid, + R.drawable.ic_compound_take_photo, + R.drawable.ic_compound_take_photo_solid, + R.drawable.ic_compound_text_formatting, + R.drawable.ic_compound_threads, + R.drawable.ic_compound_threads_solid, + R.drawable.ic_compound_time, + R.drawable.ic_compound_underline, + R.drawable.ic_compound_unknown, + R.drawable.ic_compound_unknown_solid, + R.drawable.ic_compound_unpin, + R.drawable.ic_compound_user, + R.drawable.ic_compound_user_add, + R.drawable.ic_compound_user_add_solid, + R.drawable.ic_compound_user_profile, + R.drawable.ic_compound_user_profile_solid, + R.drawable.ic_compound_user_solid, + R.drawable.ic_compound_verified, + R.drawable.ic_compound_video_call, + R.drawable.ic_compound_video_call_declined_solid, + R.drawable.ic_compound_video_call_missed_solid, + R.drawable.ic_compound_video_call_off, + R.drawable.ic_compound_video_call_off_solid, + R.drawable.ic_compound_video_call_solid, + R.drawable.ic_compound_visibility_off, + R.drawable.ic_compound_visibility_on, + R.drawable.ic_compound_voice_call, + R.drawable.ic_compound_voice_call_solid, + R.drawable.ic_compound_volume_off, + R.drawable.ic_compound_volume_off_solid, + R.drawable.ic_compound_volume_on, + R.drawable.ic_compound_volume_on_solid, + R.drawable.ic_compound_warning, + R.drawable.ic_compound_web_browser, + R.drawable.ic_compound_windows, + R.drawable.ic_compound_workspace, + R.drawable.ic_compound_workspace_solid, + ) +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/DO_NOT_MODIFY.txt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/DO_NOT_MODIFY.txt new file mode 100644 index 0000000000..a6f7dd3f6a --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/DO_NOT_MODIFY.txt @@ -0,0 +1 @@ +Files inside this package are generated automatically from the Compound project (https://github.com/vector-im/compound-design-tokens) and will be batch-replaced when new tokens are generated. diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColors.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColors.kt new file mode 100644 index 0000000000..8da51213f8 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColors.kt @@ -0,0 +1,222 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + + +@file:Suppress("all") +package io.element.android.compound.tokens.generated + +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color + + +/** + * !!! WARNING !!! + * + * THIS IS AN AUTOGENERATED FILE. + * DO NOT EDIT MANUALLY. + */ + + + + +/** + * This class holds all the semantic tokens of the Compound theme. + */ +@Immutable +data class SemanticColors( + /** Background colour for accent or brand actions. State: Hover */ + val bgAccentHovered: Color, + /** Background colour for accent or brand actions. State: Pressed */ + val bgAccentPressed: Color, + /** Background colour for accent or brand actions. State: Rest. */ + val bgAccentRest: Color, + /** Background colour for accent or brand actions. State: Selected */ + val bgAccentSelected: Color, + /** Background colour for primary actions. State: Disabled. */ + val bgActionPrimaryDisabled: Color, + /** Background colour for primary actions. State: Hover. */ + val bgActionPrimaryHovered: Color, + /** Background colour for primary actions. State: Pressed. */ + val bgActionPrimaryPressed: Color, + /** Background colour for primary actions. State: Rest. */ + val bgActionPrimaryRest: Color, + /** Background colour for secondary actions. State: Hover. */ + val bgActionSecondaryHovered: Color, + /** Background colour for secondary actions. State: Pressed. */ + val bgActionSecondaryPressed: Color, + /** Background colour for secondary actions. State: Rest. */ + val bgActionSecondaryRest: Color, + /** Badge accent background colour */ + val bgBadgeAccent: Color, + /** Badge default background colour */ + val bgBadgeDefault: Color, + /** Badge info background colour */ + val bgBadgeInfo: Color, + /** Default global background for the user interface. +Elevation: Default (Level 0) */ + val bgCanvasDefault: Color, + /** Default global background for the user interface. +Elevation: Level 1. */ + val bgCanvasDefaultLevel1: Color, + /** Default background for disabled elements. There's no minimum contrast requirement. */ + val bgCanvasDisabled: Color, + /** High-contrast background color for critical state. State: Hover. */ + val bgCriticalHovered: Color, + /** High-contrast background color for critical state. State: Rest. */ + val bgCriticalPrimary: Color, + /** Default subtle critical surfaces. State: Rest. */ + val bgCriticalSubtle: Color, + /** Default subtle critical surfaces. State: Hover. */ + val bgCriticalSubtleHovered: Color, + /** Decorative background (1, Lime) for avatars and usernames. */ + val bgDecorative1: Color, + /** Decorative background (2, Cyan) for avatars and usernames. */ + val bgDecorative2: Color, + /** Decorative background (3, Fuchsia) for avatars and usernames. */ + val bgDecorative3: Color, + /** Decorative background (4, Purple) for avatars and usernames. */ + val bgDecorative4: Color, + /** Decorative background (5, Pink) for avatars and usernames. */ + val bgDecorative5: Color, + /** Decorative background (6, Orange) for avatars and usernames. */ + val bgDecorative6: Color, + /** Subtle background colour for informational elements. State: Rest. */ + val bgInfoSubtle: Color, + /** Medium contrast surfaces. +Elevation: Default (Level 2). */ + val bgSubtlePrimary: Color, + /** Low contrast surfaces. +Elevation: Default (Level 1). */ + val bgSubtleSecondary: Color, + /** Lower contrast surfaces. +Elevation: Level 0. */ + val bgSubtleSecondaryLevel0: Color, + /** Subtle background colour for success state elements. State: Rest. */ + val bgSuccessSubtle: Color, + /** accent border intended for keylines on message highlights */ + val borderAccentSubtle: Color, + /** High-contrast border for critical state. State: Hover. */ + val borderCriticalHovered: Color, + /** High-contrast border for critical state. State: Rest. */ + val borderCriticalPrimary: Color, + /** Subtle border colour for critical state elements. */ + val borderCriticalSubtle: Color, + /** Used for borders of disabled elements. There's no minimum contrast requirement. */ + val borderDisabled: Color, + /** Used for the focus state outline. */ + val borderFocused: Color, + /** Subtle border colour for informational elements. */ + val borderInfoSubtle: Color, + /** Default contrast for accessible interactive element borders. State: Hover. */ + val borderInteractiveHovered: Color, + /** Default contrast for accessible interactive element borders. State: Rest. */ + val borderInteractivePrimary: Color, + /** ⚠️ Lowest contrast for non-accessible interactive element borders, <3:1. Only use for non-essential borders. Do not rely exclusively on them. State: Rest. */ + val borderInteractiveSecondary: Color, + /** Subtle border colour for success state elements. */ + val borderSuccessSubtle: Color, + /** Background gradient stop for super and send buttons */ + val gradientActionStop1: Color, + /** Background gradient stop for super and send buttons */ + val gradientActionStop2: Color, + /** Background gradient stop for super and send buttons */ + val gradientActionStop3: Color, + /** Background gradient stop for super and send buttons */ + val gradientActionStop4: Color, + /** Subtle background gradient stop for info */ + val gradientInfoStop1: Color, + /** Subtle background gradient stop for info */ + val gradientInfoStop2: Color, + /** Subtle background gradient stop for info */ + val gradientInfoStop3: Color, + /** Subtle background gradient stop for info */ + val gradientInfoStop4: Color, + /** Subtle background gradient stop for info */ + val gradientInfoStop5: Color, + /** Subtle background gradient stop for info */ + val gradientInfoStop6: Color, + /** Subtle background gradient stop for message highlight and bloom */ + val gradientSubtleStop1: Color, + /** Subtle background gradient stop for message highlight and bloom */ + val gradientSubtleStop2: Color, + /** Subtle background gradient stop for message highlight and bloom */ + val gradientSubtleStop3: Color, + /** Subtle background gradient stop for message highlight and bloom */ + val gradientSubtleStop4: Color, + /** Subtle background gradient stop for message highlight and bloom */ + val gradientSubtleStop5: Color, + /** Subtle background gradient stop for message highlight and bloom */ + val gradientSubtleStop6: Color, + /** Highest contrast accessible accent icons. */ + val iconAccentPrimary: Color, + /** Lowest contrast accessible accent icons. */ + val iconAccentTertiary: Color, + /** High-contrast icon for critical state. State: Rest. */ + val iconCriticalPrimary: Color, + /** Use for icons in disabled elements. There's no minimum contrast requirement. */ + val iconDisabled: Color, + /** High-contrast icon for informational elements. */ + val iconInfoPrimary: Color, + /** Highest contrast icon color on top of high-contrast solid backgrounds like primary, accent, or destructive actions. */ + val iconOnSolidPrimary: Color, + /** Highest contrast icons. */ + val iconPrimary: Color, + /** Translucent version of primary icon. Refer to it for intended use. */ + val iconPrimaryAlpha: Color, + /** ⚠️ Lowest contrast non-accessible icons, <3:1. Only use for non-essential icons. Do not rely exclusively on them. */ + val iconQuaternary: Color, + /** Translucent version of quaternary icon. Refer to it for intended use. */ + val iconQuaternaryAlpha: Color, + /** Lower contrast icons. */ + val iconSecondary: Color, + /** Translucent version of secondary icon. Refer to it for intended use. */ + val iconSecondaryAlpha: Color, + /** High-contrast icon for success state elements. */ + val iconSuccessPrimary: Color, + /** Lowest contrast accessible icons. */ + val iconTertiary: Color, + /** Translucent version of tertiary icon. Refer to it for intended use. */ + val iconTertiaryAlpha: Color, + /** Accent text colour for plain actions. */ + val textActionAccent: Color, + /** Default text colour for plain actions. */ + val textActionPrimary: Color, + /** Badge accent text colour */ + val textBadgeAccent: Color, + /** Badge info text colour */ + val textBadgeInfo: Color, + /** Text colour for destructive plain actions. */ + val textCriticalPrimary: Color, + /** Decorative text colour (1, Lime) for avatars and usernames. */ + val textDecorative1: Color, + /** Decorative text colour (2, Cyan) for avatars and usernames. */ + val textDecorative2: Color, + /** Decorative text colour (3, Fuchsia) for avatars and usernames. */ + val textDecorative3: Color, + /** Decorative text colour (4, Purple) for avatars and usernames. */ + val textDecorative4: Color, + /** Decorative text colour (5, Pink) for avatars and usernames. */ + val textDecorative5: Color, + /** Decorative text colour (6, Orange) for avatars and usernames. */ + val textDecorative6: Color, + /** Use for regular text in disabled elements. There's no minimum contrast requirement. */ + val textDisabled: Color, + /** Accent text colour for informational elements. */ + val textInfoPrimary: Color, + /** Text colour for external links. */ + val textLinkExternal: Color, + /** For use as text color on top of high-contrast solid backgrounds like primary, accent, or destructive actions. */ + val textOnSolidPrimary: Color, + /** Highest contrast text. */ + val textPrimary: Color, + /** Lowest contrast text. */ + val textSecondary: Color, + /** Accent text colour for success state elements. */ + val textSuccessPrimary: Color, + /** True for light theme, false for dark theme. */ + val isLight: Boolean, +) diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsDark.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsDark.kt new file mode 100644 index 0000000000..5ccdfaa308 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsDark.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +/** + * !!! WARNING !!! + * + * THIS IS AN AUTOGENERATED FILE. + * DO NOT EDIT MANUALLY. + */ + + + +@file:Suppress("all") +package io.element.android.compound.tokens.generated + +import io.element.android.compound.annotations.CoreColorToken +import io.element.android.compound.tokens.generated.internal.DarkColorTokens + +/** + * Semantic colors for the dark Compound theme. + */ +@OptIn(CoreColorToken::class) +val compoundColorsDark = SemanticColors( + bgAccentHovered = DarkColorTokens.colorGreen1000, + bgAccentPressed = DarkColorTokens.colorGreen1100, + bgAccentRest = DarkColorTokens.colorGreen900, + bgAccentSelected = DarkColorTokens.colorAlphaGreen300, + bgActionPrimaryDisabled = DarkColorTokens.colorGray700, + bgActionPrimaryHovered = DarkColorTokens.colorGray1200, + bgActionPrimaryPressed = DarkColorTokens.colorGray1100, + bgActionPrimaryRest = DarkColorTokens.colorGray1400, + bgActionSecondaryHovered = DarkColorTokens.colorAlphaGray200, + bgActionSecondaryPressed = DarkColorTokens.colorAlphaGray300, + bgActionSecondaryRest = DarkColorTokens.colorThemeBg, + bgBadgeAccent = DarkColorTokens.colorAlphaGreen300, + bgBadgeDefault = DarkColorTokens.colorAlphaGray300, + bgBadgeInfo = DarkColorTokens.colorAlphaBlue300, + bgCanvasDefault = DarkColorTokens.colorThemeBg, + bgCanvasDefaultLevel1 = DarkColorTokens.colorGray300, + bgCanvasDisabled = DarkColorTokens.colorGray200, + bgCriticalHovered = DarkColorTokens.colorRed1000, + bgCriticalPrimary = DarkColorTokens.colorRed900, + bgCriticalSubtle = DarkColorTokens.colorRed200, + bgCriticalSubtleHovered = DarkColorTokens.colorRed300, + bgDecorative1 = DarkColorTokens.colorLime300, + bgDecorative2 = DarkColorTokens.colorCyan300, + bgDecorative3 = DarkColorTokens.colorFuchsia300, + bgDecorative4 = DarkColorTokens.colorPurple300, + bgDecorative5 = DarkColorTokens.colorPink300, + bgDecorative6 = DarkColorTokens.colorOrange300, + bgInfoSubtle = DarkColorTokens.colorBlue200, + bgSubtlePrimary = DarkColorTokens.colorGray400, + bgSubtleSecondary = DarkColorTokens.colorGray300, + bgSubtleSecondaryLevel0 = DarkColorTokens.colorThemeBg, + bgSuccessSubtle = DarkColorTokens.colorGreen200, + borderAccentSubtle = DarkColorTokens.colorGreen700, + borderCriticalHovered = DarkColorTokens.colorRed1000, + borderCriticalPrimary = DarkColorTokens.colorRed900, + borderCriticalSubtle = DarkColorTokens.colorRed500, + borderDisabled = DarkColorTokens.colorGray500, + borderFocused = DarkColorTokens.colorBlue900, + borderInfoSubtle = DarkColorTokens.colorBlue700, + borderInteractiveHovered = DarkColorTokens.colorGray1100, + borderInteractivePrimary = DarkColorTokens.colorGray800, + borderInteractiveSecondary = DarkColorTokens.colorGray600, + borderSuccessSubtle = DarkColorTokens.colorGreen500, + gradientActionStop1 = DarkColorTokens.colorGreen1100, + gradientActionStop2 = DarkColorTokens.colorGreen900, + gradientActionStop3 = DarkColorTokens.colorGreen700, + gradientActionStop4 = DarkColorTokens.colorGreen500, + gradientInfoStop1 = DarkColorTokens.colorAlphaBlue500, + gradientInfoStop2 = DarkColorTokens.colorAlphaBlue400, + gradientInfoStop3 = DarkColorTokens.colorAlphaBlue300, + gradientInfoStop4 = DarkColorTokens.colorAlphaBlue200, + gradientInfoStop5 = DarkColorTokens.colorAlphaBlue100, + gradientInfoStop6 = DarkColorTokens.colorTransparent, + gradientSubtleStop1 = DarkColorTokens.colorAlphaGreen500, + gradientSubtleStop2 = DarkColorTokens.colorAlphaGreen400, + gradientSubtleStop3 = DarkColorTokens.colorAlphaGreen300, + gradientSubtleStop4 = DarkColorTokens.colorAlphaGreen200, + gradientSubtleStop5 = DarkColorTokens.colorAlphaGreen100, + gradientSubtleStop6 = DarkColorTokens.colorTransparent, + iconAccentPrimary = DarkColorTokens.colorGreen900, + iconAccentTertiary = DarkColorTokens.colorGreen800, + iconCriticalPrimary = DarkColorTokens.colorRed900, + iconDisabled = DarkColorTokens.colorGray700, + iconInfoPrimary = DarkColorTokens.colorBlue900, + iconOnSolidPrimary = DarkColorTokens.colorThemeBg, + iconPrimary = DarkColorTokens.colorGray1400, + iconPrimaryAlpha = DarkColorTokens.colorAlphaGray1400, + iconQuaternary = DarkColorTokens.colorGray700, + iconQuaternaryAlpha = DarkColorTokens.colorAlphaGray700, + iconSecondary = DarkColorTokens.colorGray900, + iconSecondaryAlpha = DarkColorTokens.colorAlphaGray900, + iconSuccessPrimary = DarkColorTokens.colorGreen900, + iconTertiary = DarkColorTokens.colorGray800, + iconTertiaryAlpha = DarkColorTokens.colorAlphaGray800, + textActionAccent = DarkColorTokens.colorGreen900, + textActionPrimary = DarkColorTokens.colorGray1400, + textBadgeAccent = DarkColorTokens.colorGreen1100, + textBadgeInfo = DarkColorTokens.colorBlue1100, + textCriticalPrimary = DarkColorTokens.colorRed900, + textDecorative1 = DarkColorTokens.colorLime1100, + textDecorative2 = DarkColorTokens.colorCyan1100, + textDecorative3 = DarkColorTokens.colorFuchsia1100, + textDecorative4 = DarkColorTokens.colorPurple1100, + textDecorative5 = DarkColorTokens.colorPink1100, + textDecorative6 = DarkColorTokens.colorOrange1100, + textDisabled = DarkColorTokens.colorGray800, + textInfoPrimary = DarkColorTokens.colorBlue900, + textLinkExternal = DarkColorTokens.colorBlue900, + textOnSolidPrimary = DarkColorTokens.colorThemeBg, + textPrimary = DarkColorTokens.colorGray1400, + textSecondary = DarkColorTokens.colorGray900, + textSuccessPrimary = DarkColorTokens.colorGreen900, + isLight = false, +) diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsDarkHc.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsDarkHc.kt new file mode 100644 index 0000000000..88d2ef3abe --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsDarkHc.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +/** + * !!! WARNING !!! + * + * THIS IS AN AUTOGENERATED FILE. + * DO NOT EDIT MANUALLY. + */ + + + +@file:Suppress("all") +package io.element.android.compound.tokens.generated + +import io.element.android.compound.annotations.CoreColorToken +import io.element.android.compound.tokens.generated.internal.DarkHcColorTokens + +/** + * Semantic colors for the high contrast dark Compound theme. + */ +@OptIn(CoreColorToken::class) +val compoundColorsHcDark = SemanticColors( + bgAccentHovered = DarkHcColorTokens.colorGreen1000, + bgAccentPressed = DarkHcColorTokens.colorGreen1100, + bgAccentRest = DarkHcColorTokens.colorGreen900, + bgAccentSelected = DarkHcColorTokens.colorAlphaGreen300, + bgActionPrimaryDisabled = DarkHcColorTokens.colorGray700, + bgActionPrimaryHovered = DarkHcColorTokens.colorGray1200, + bgActionPrimaryPressed = DarkHcColorTokens.colorGray1100, + bgActionPrimaryRest = DarkHcColorTokens.colorGray1400, + bgActionSecondaryHovered = DarkHcColorTokens.colorAlphaGray200, + bgActionSecondaryPressed = DarkHcColorTokens.colorAlphaGray300, + bgActionSecondaryRest = DarkHcColorTokens.colorThemeBg, + bgBadgeAccent = DarkHcColorTokens.colorAlphaGreen300, + bgBadgeDefault = DarkHcColorTokens.colorAlphaGray300, + bgBadgeInfo = DarkHcColorTokens.colorAlphaBlue300, + bgCanvasDefault = DarkHcColorTokens.colorThemeBg, + bgCanvasDefaultLevel1 = DarkHcColorTokens.colorGray300, + bgCanvasDisabled = DarkHcColorTokens.colorGray200, + bgCriticalHovered = DarkHcColorTokens.colorRed1000, + bgCriticalPrimary = DarkHcColorTokens.colorRed900, + bgCriticalSubtle = DarkHcColorTokens.colorRed200, + bgCriticalSubtleHovered = DarkHcColorTokens.colorRed300, + bgDecorative1 = DarkHcColorTokens.colorLime300, + bgDecorative2 = DarkHcColorTokens.colorCyan300, + bgDecorative3 = DarkHcColorTokens.colorFuchsia300, + bgDecorative4 = DarkHcColorTokens.colorPurple300, + bgDecorative5 = DarkHcColorTokens.colorPink300, + bgDecorative6 = DarkHcColorTokens.colorOrange300, + bgInfoSubtle = DarkHcColorTokens.colorBlue200, + bgSubtlePrimary = DarkHcColorTokens.colorGray400, + bgSubtleSecondary = DarkHcColorTokens.colorGray300, + bgSubtleSecondaryLevel0 = DarkHcColorTokens.colorThemeBg, + bgSuccessSubtle = DarkHcColorTokens.colorGreen200, + borderAccentSubtle = DarkHcColorTokens.colorGreen700, + borderCriticalHovered = DarkHcColorTokens.colorRed1000, + borderCriticalPrimary = DarkHcColorTokens.colorRed900, + borderCriticalSubtle = DarkHcColorTokens.colorRed500, + borderDisabled = DarkHcColorTokens.colorGray500, + borderFocused = DarkHcColorTokens.colorBlue900, + borderInfoSubtle = DarkHcColorTokens.colorBlue700, + borderInteractiveHovered = DarkHcColorTokens.colorGray1100, + borderInteractivePrimary = DarkHcColorTokens.colorGray800, + borderInteractiveSecondary = DarkHcColorTokens.colorGray600, + borderSuccessSubtle = DarkHcColorTokens.colorGreen500, + gradientActionStop1 = DarkHcColorTokens.colorGreen1100, + gradientActionStop2 = DarkHcColorTokens.colorGreen900, + gradientActionStop3 = DarkHcColorTokens.colorGreen700, + gradientActionStop4 = DarkHcColorTokens.colorGreen500, + gradientInfoStop1 = DarkHcColorTokens.colorAlphaBlue500, + gradientInfoStop2 = DarkHcColorTokens.colorAlphaBlue400, + gradientInfoStop3 = DarkHcColorTokens.colorAlphaBlue300, + gradientInfoStop4 = DarkHcColorTokens.colorAlphaBlue200, + gradientInfoStop5 = DarkHcColorTokens.colorAlphaBlue100, + gradientInfoStop6 = DarkHcColorTokens.colorTransparent, + gradientSubtleStop1 = DarkHcColorTokens.colorAlphaGreen500, + gradientSubtleStop2 = DarkHcColorTokens.colorAlphaGreen400, + gradientSubtleStop3 = DarkHcColorTokens.colorAlphaGreen300, + gradientSubtleStop4 = DarkHcColorTokens.colorAlphaGreen200, + gradientSubtleStop5 = DarkHcColorTokens.colorAlphaGreen100, + gradientSubtleStop6 = DarkHcColorTokens.colorTransparent, + iconAccentPrimary = DarkHcColorTokens.colorGreen900, + iconAccentTertiary = DarkHcColorTokens.colorGreen800, + iconCriticalPrimary = DarkHcColorTokens.colorRed900, + iconDisabled = DarkHcColorTokens.colorGray700, + iconInfoPrimary = DarkHcColorTokens.colorBlue900, + iconOnSolidPrimary = DarkHcColorTokens.colorThemeBg, + iconPrimary = DarkHcColorTokens.colorGray1400, + iconPrimaryAlpha = DarkHcColorTokens.colorAlphaGray1400, + iconQuaternary = DarkHcColorTokens.colorGray700, + iconQuaternaryAlpha = DarkHcColorTokens.colorAlphaGray700, + iconSecondary = DarkHcColorTokens.colorGray900, + iconSecondaryAlpha = DarkHcColorTokens.colorAlphaGray900, + iconSuccessPrimary = DarkHcColorTokens.colorGreen900, + iconTertiary = DarkHcColorTokens.colorGray800, + iconTertiaryAlpha = DarkHcColorTokens.colorAlphaGray800, + textActionAccent = DarkHcColorTokens.colorGreen900, + textActionPrimary = DarkHcColorTokens.colorGray1400, + textBadgeAccent = DarkHcColorTokens.colorGreen1100, + textBadgeInfo = DarkHcColorTokens.colorBlue1100, + textCriticalPrimary = DarkHcColorTokens.colorRed900, + textDecorative1 = DarkHcColorTokens.colorLime1100, + textDecorative2 = DarkHcColorTokens.colorCyan1100, + textDecorative3 = DarkHcColorTokens.colorFuchsia1100, + textDecorative4 = DarkHcColorTokens.colorPurple1100, + textDecorative5 = DarkHcColorTokens.colorPink1100, + textDecorative6 = DarkHcColorTokens.colorOrange1100, + textDisabled = DarkHcColorTokens.colorGray800, + textInfoPrimary = DarkHcColorTokens.colorBlue900, + textLinkExternal = DarkHcColorTokens.colorBlue900, + textOnSolidPrimary = DarkHcColorTokens.colorThemeBg, + textPrimary = DarkHcColorTokens.colorGray1400, + textSecondary = DarkHcColorTokens.colorGray900, + textSuccessPrimary = DarkHcColorTokens.colorGreen900, + isLight = false, +) diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsLight.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsLight.kt new file mode 100644 index 0000000000..03173ad24e --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsLight.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +/** + * !!! WARNING !!! + * + * THIS IS AN AUTOGENERATED FILE. + * DO NOT EDIT MANUALLY. + */ + + + +@file:Suppress("all") +package io.element.android.compound.tokens.generated + +import io.element.android.compound.annotations.CoreColorToken +import io.element.android.compound.tokens.generated.internal.LightColorTokens + +/** + * Semantic colors for the light Compound theme. + */ +@OptIn(CoreColorToken::class) +val compoundColorsLight = SemanticColors( + bgAccentHovered = LightColorTokens.colorGreen1000, + bgAccentPressed = LightColorTokens.colorGreen1100, + bgAccentRest = LightColorTokens.colorGreen900, + bgAccentSelected = LightColorTokens.colorAlphaGreen300, + bgActionPrimaryDisabled = LightColorTokens.colorGray700, + bgActionPrimaryHovered = LightColorTokens.colorGray1200, + bgActionPrimaryPressed = LightColorTokens.colorGray1100, + bgActionPrimaryRest = LightColorTokens.colorGray1400, + bgActionSecondaryHovered = LightColorTokens.colorAlphaGray200, + bgActionSecondaryPressed = LightColorTokens.colorAlphaGray300, + bgActionSecondaryRest = LightColorTokens.colorThemeBg, + bgBadgeAccent = LightColorTokens.colorAlphaGreen300, + bgBadgeDefault = LightColorTokens.colorAlphaGray300, + bgBadgeInfo = LightColorTokens.colorAlphaBlue300, + bgCanvasDefault = LightColorTokens.colorThemeBg, + bgCanvasDefaultLevel1 = LightColorTokens.colorThemeBg, + bgCanvasDisabled = LightColorTokens.colorGray200, + bgCriticalHovered = LightColorTokens.colorRed1000, + bgCriticalPrimary = LightColorTokens.colorRed900, + bgCriticalSubtle = LightColorTokens.colorRed200, + bgCriticalSubtleHovered = LightColorTokens.colorRed300, + bgDecorative1 = LightColorTokens.colorLime300, + bgDecorative2 = LightColorTokens.colorCyan300, + bgDecorative3 = LightColorTokens.colorFuchsia300, + bgDecorative4 = LightColorTokens.colorPurple300, + bgDecorative5 = LightColorTokens.colorPink300, + bgDecorative6 = LightColorTokens.colorOrange300, + bgInfoSubtle = LightColorTokens.colorBlue200, + bgSubtlePrimary = LightColorTokens.colorGray400, + bgSubtleSecondary = LightColorTokens.colorGray300, + bgSubtleSecondaryLevel0 = LightColorTokens.colorGray300, + bgSuccessSubtle = LightColorTokens.colorGreen200, + borderAccentSubtle = LightColorTokens.colorGreen700, + borderCriticalHovered = LightColorTokens.colorRed1000, + borderCriticalPrimary = LightColorTokens.colorRed900, + borderCriticalSubtle = LightColorTokens.colorRed500, + borderDisabled = LightColorTokens.colorGray500, + borderFocused = LightColorTokens.colorBlue900, + borderInfoSubtle = LightColorTokens.colorBlue700, + borderInteractiveHovered = LightColorTokens.colorGray1100, + borderInteractivePrimary = LightColorTokens.colorGray800, + borderInteractiveSecondary = LightColorTokens.colorGray600, + borderSuccessSubtle = LightColorTokens.colorGreen500, + gradientActionStop1 = LightColorTokens.colorGreen500, + gradientActionStop2 = LightColorTokens.colorGreen700, + gradientActionStop3 = LightColorTokens.colorGreen900, + gradientActionStop4 = LightColorTokens.colorGreen1100, + gradientInfoStop1 = LightColorTokens.colorAlphaBlue500, + gradientInfoStop2 = LightColorTokens.colorAlphaBlue400, + gradientInfoStop3 = LightColorTokens.colorAlphaBlue300, + gradientInfoStop4 = LightColorTokens.colorAlphaBlue200, + gradientInfoStop5 = LightColorTokens.colorAlphaBlue100, + gradientInfoStop6 = LightColorTokens.colorTransparent, + gradientSubtleStop1 = LightColorTokens.colorAlphaGreen500, + gradientSubtleStop2 = LightColorTokens.colorAlphaGreen400, + gradientSubtleStop3 = LightColorTokens.colorAlphaGreen300, + gradientSubtleStop4 = LightColorTokens.colorAlphaGreen200, + gradientSubtleStop5 = LightColorTokens.colorAlphaGreen100, + gradientSubtleStop6 = LightColorTokens.colorTransparent, + iconAccentPrimary = LightColorTokens.colorGreen900, + iconAccentTertiary = LightColorTokens.colorGreen800, + iconCriticalPrimary = LightColorTokens.colorRed900, + iconDisabled = LightColorTokens.colorGray700, + iconInfoPrimary = LightColorTokens.colorBlue900, + iconOnSolidPrimary = LightColorTokens.colorThemeBg, + iconPrimary = LightColorTokens.colorGray1400, + iconPrimaryAlpha = LightColorTokens.colorAlphaGray1400, + iconQuaternary = LightColorTokens.colorGray700, + iconQuaternaryAlpha = LightColorTokens.colorAlphaGray700, + iconSecondary = LightColorTokens.colorGray900, + iconSecondaryAlpha = LightColorTokens.colorAlphaGray900, + iconSuccessPrimary = LightColorTokens.colorGreen900, + iconTertiary = LightColorTokens.colorGray800, + iconTertiaryAlpha = LightColorTokens.colorAlphaGray800, + textActionAccent = LightColorTokens.colorGreen900, + textActionPrimary = LightColorTokens.colorGray1400, + textBadgeAccent = LightColorTokens.colorGreen1100, + textBadgeInfo = LightColorTokens.colorBlue1100, + textCriticalPrimary = LightColorTokens.colorRed900, + textDecorative1 = LightColorTokens.colorLime1100, + textDecorative2 = LightColorTokens.colorCyan1100, + textDecorative3 = LightColorTokens.colorFuchsia1100, + textDecorative4 = LightColorTokens.colorPurple1100, + textDecorative5 = LightColorTokens.colorPink1100, + textDecorative6 = LightColorTokens.colorOrange1100, + textDisabled = LightColorTokens.colorGray800, + textInfoPrimary = LightColorTokens.colorBlue900, + textLinkExternal = LightColorTokens.colorBlue900, + textOnSolidPrimary = LightColorTokens.colorThemeBg, + textPrimary = LightColorTokens.colorGray1400, + textSecondary = LightColorTokens.colorGray900, + textSuccessPrimary = LightColorTokens.colorGreen900, + isLight = true, +) diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsLightHc.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsLightHc.kt new file mode 100644 index 0000000000..00100f7ea8 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsLightHc.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +/** + * !!! WARNING !!! + * + * THIS IS AN AUTOGENERATED FILE. + * DO NOT EDIT MANUALLY. + */ + + + +@file:Suppress("all") +package io.element.android.compound.tokens.generated + +import io.element.android.compound.annotations.CoreColorToken +import io.element.android.compound.tokens.generated.internal.LightHcColorTokens + +/** + * Semantic colors for the high contrast light Compound theme. + */ +@OptIn(CoreColorToken::class) +val compoundColorsHcLight = SemanticColors( + bgAccentHovered = LightHcColorTokens.colorGreen1000, + bgAccentPressed = LightHcColorTokens.colorGreen1100, + bgAccentRest = LightHcColorTokens.colorGreen900, + bgAccentSelected = LightHcColorTokens.colorAlphaGreen300, + bgActionPrimaryDisabled = LightHcColorTokens.colorGray700, + bgActionPrimaryHovered = LightHcColorTokens.colorGray1200, + bgActionPrimaryPressed = LightHcColorTokens.colorGray1100, + bgActionPrimaryRest = LightHcColorTokens.colorGray1400, + bgActionSecondaryHovered = LightHcColorTokens.colorAlphaGray200, + bgActionSecondaryPressed = LightHcColorTokens.colorAlphaGray300, + bgActionSecondaryRest = LightHcColorTokens.colorThemeBg, + bgBadgeAccent = LightHcColorTokens.colorAlphaGreen300, + bgBadgeDefault = LightHcColorTokens.colorAlphaGray300, + bgBadgeInfo = LightHcColorTokens.colorAlphaBlue300, + bgCanvasDefault = LightHcColorTokens.colorThemeBg, + bgCanvasDefaultLevel1 = LightHcColorTokens.colorThemeBg, + bgCanvasDisabled = LightHcColorTokens.colorGray200, + bgCriticalHovered = LightHcColorTokens.colorRed1000, + bgCriticalPrimary = LightHcColorTokens.colorRed900, + bgCriticalSubtle = LightHcColorTokens.colorRed200, + bgCriticalSubtleHovered = LightHcColorTokens.colorRed300, + bgDecorative1 = LightHcColorTokens.colorLime300, + bgDecorative2 = LightHcColorTokens.colorCyan300, + bgDecorative3 = LightHcColorTokens.colorFuchsia300, + bgDecorative4 = LightHcColorTokens.colorPurple300, + bgDecorative5 = LightHcColorTokens.colorPink300, + bgDecorative6 = LightHcColorTokens.colorOrange300, + bgInfoSubtle = LightHcColorTokens.colorBlue200, + bgSubtlePrimary = LightHcColorTokens.colorGray400, + bgSubtleSecondary = LightHcColorTokens.colorGray300, + bgSubtleSecondaryLevel0 = LightHcColorTokens.colorGray300, + bgSuccessSubtle = LightHcColorTokens.colorGreen200, + borderAccentSubtle = LightHcColorTokens.colorGreen700, + borderCriticalHovered = LightHcColorTokens.colorRed1000, + borderCriticalPrimary = LightHcColorTokens.colorRed900, + borderCriticalSubtle = LightHcColorTokens.colorRed500, + borderDisabled = LightHcColorTokens.colorGray500, + borderFocused = LightHcColorTokens.colorBlue900, + borderInfoSubtle = LightHcColorTokens.colorBlue700, + borderInteractiveHovered = LightHcColorTokens.colorGray1100, + borderInteractivePrimary = LightHcColorTokens.colorGray800, + borderInteractiveSecondary = LightHcColorTokens.colorGray600, + borderSuccessSubtle = LightHcColorTokens.colorGreen500, + gradientActionStop1 = LightHcColorTokens.colorGreen500, + gradientActionStop2 = LightHcColorTokens.colorGreen700, + gradientActionStop3 = LightHcColorTokens.colorGreen900, + gradientActionStop4 = LightHcColorTokens.colorGreen1100, + gradientInfoStop1 = LightHcColorTokens.colorAlphaBlue500, + gradientInfoStop2 = LightHcColorTokens.colorAlphaBlue400, + gradientInfoStop3 = LightHcColorTokens.colorAlphaBlue300, + gradientInfoStop4 = LightHcColorTokens.colorAlphaBlue200, + gradientInfoStop5 = LightHcColorTokens.colorAlphaBlue100, + gradientInfoStop6 = LightHcColorTokens.colorTransparent, + gradientSubtleStop1 = LightHcColorTokens.colorAlphaGreen500, + gradientSubtleStop2 = LightHcColorTokens.colorAlphaGreen400, + gradientSubtleStop3 = LightHcColorTokens.colorAlphaGreen300, + gradientSubtleStop4 = LightHcColorTokens.colorAlphaGreen200, + gradientSubtleStop5 = LightHcColorTokens.colorAlphaGreen100, + gradientSubtleStop6 = LightHcColorTokens.colorTransparent, + iconAccentPrimary = LightHcColorTokens.colorGreen900, + iconAccentTertiary = LightHcColorTokens.colorGreen800, + iconCriticalPrimary = LightHcColorTokens.colorRed900, + iconDisabled = LightHcColorTokens.colorGray700, + iconInfoPrimary = LightHcColorTokens.colorBlue900, + iconOnSolidPrimary = LightHcColorTokens.colorThemeBg, + iconPrimary = LightHcColorTokens.colorGray1400, + iconPrimaryAlpha = LightHcColorTokens.colorAlphaGray1400, + iconQuaternary = LightHcColorTokens.colorGray700, + iconQuaternaryAlpha = LightHcColorTokens.colorAlphaGray700, + iconSecondary = LightHcColorTokens.colorGray900, + iconSecondaryAlpha = LightHcColorTokens.colorAlphaGray900, + iconSuccessPrimary = LightHcColorTokens.colorGreen900, + iconTertiary = LightHcColorTokens.colorGray800, + iconTertiaryAlpha = LightHcColorTokens.colorAlphaGray800, + textActionAccent = LightHcColorTokens.colorGreen900, + textActionPrimary = LightHcColorTokens.colorGray1400, + textBadgeAccent = LightHcColorTokens.colorGreen1100, + textBadgeInfo = LightHcColorTokens.colorBlue1100, + textCriticalPrimary = LightHcColorTokens.colorRed900, + textDecorative1 = LightHcColorTokens.colorLime1100, + textDecorative2 = LightHcColorTokens.colorCyan1100, + textDecorative3 = LightHcColorTokens.colorFuchsia1100, + textDecorative4 = LightHcColorTokens.colorPurple1100, + textDecorative5 = LightHcColorTokens.colorPink1100, + textDecorative6 = LightHcColorTokens.colorOrange1100, + textDisabled = LightHcColorTokens.colorGray800, + textInfoPrimary = LightHcColorTokens.colorBlue900, + textLinkExternal = LightHcColorTokens.colorBlue900, + textOnSolidPrimary = LightHcColorTokens.colorThemeBg, + textPrimary = LightHcColorTokens.colorGray1400, + textSecondary = LightHcColorTokens.colorGray900, + textSuccessPrimary = LightHcColorTokens.colorGreen900, + isLight = true, +) diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/TypographyTokens.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/TypographyTokens.kt new file mode 100644 index 0000000000..a25b79234c --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/TypographyTokens.kt @@ -0,0 +1,174 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + + +/** + * !!! WARNING !!! + * + * THIS IS AN AUTOGENERATED FILE. + * DO NOT EDIT MANUALLY. + */ + + + +@file:Suppress("all") +package io.element.android.compound.tokens.generated + +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.em +import androidx.compose.ui.unit.sp +import androidx.compose.ui.text.PlatformTextStyle +import androidx.compose.ui.text.style.LineHeightStyle + +object TypographyTokens { + val fontBodyLgMedium = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W500, + lineHeight = 22.sp, + fontSize = 16.sp, + letterSpacing = 0.015625.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) + ) + val fontBodyLgRegular = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W400, + lineHeight = 22.sp, + fontSize = 16.sp, + letterSpacing = 0.015625.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) + ) + val fontBodyMdMedium = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W500, + lineHeight = 20.sp, + fontSize = 14.sp, + letterSpacing = 0.017857.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) + ) + val fontBodyMdRegular = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W400, + lineHeight = 20.sp, + fontSize = 14.sp, + letterSpacing = 0.017857.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) + ) + val fontBodySmMedium = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W500, + lineHeight = 17.sp, + fontSize = 12.sp, + letterSpacing = 0.033333.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) + ) + val fontBodySmRegular = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W400, + lineHeight = 17.sp, + fontSize = 12.sp, + letterSpacing = 0.033333.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) + ) + val fontBodyXsMedium = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W500, + lineHeight = 15.sp, + fontSize = 11.sp, + letterSpacing = 0.045454.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) + ) + val fontBodyXsRegular = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W400, + lineHeight = 15.sp, + fontSize = 11.sp, + letterSpacing = 0.045454.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) + ) + val fontHeadingLgBold = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W700, + lineHeight = 34.sp, + fontSize = 28.sp, + letterSpacing = 0.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) + ) + val fontHeadingLgRegular = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W400, + lineHeight = 34.sp, + fontSize = 28.sp, + letterSpacing = 0.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) + ) + val fontHeadingMdBold = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W700, + lineHeight = 27.sp, + fontSize = 22.sp, + letterSpacing = 0.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) + ) + val fontHeadingMdRegular = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W400, + lineHeight = 27.sp, + fontSize = 22.sp, + letterSpacing = 0.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) + ) + val fontHeadingSmMedium = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W500, + lineHeight = 25.sp, + fontSize = 20.sp, + letterSpacing = 0.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) + ) + val fontHeadingSmRegular = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W400, + lineHeight = 25.sp, + fontSize = 20.sp, + letterSpacing = 0.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) + ) + val fontHeadingXlBold = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W700, + lineHeight = 41.sp, + fontSize = 34.sp, + letterSpacing = 0.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) + ) + val fontHeadingXlRegular = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W400, + lineHeight = 41.sp, + fontSize = 34.sp, + letterSpacing = 0.em, + platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None) + ) +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/internal/DarkColorTokens.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/internal/DarkColorTokens.kt new file mode 100644 index 0000000000..1272c0df16 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/internal/DarkColorTokens.kt @@ -0,0 +1,335 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +/** + * !!! WARNING !!! + * + * THIS IS AN AUTOGENERATED FILE. + * DO NOT EDIT MANUALLY. + */ + + + +@file:Suppress("all") +package io.element.android.compound.tokens.generated.internal + +import androidx.compose.ui.graphics.Color +import io.element.android.compound.annotations.CoreColorToken + +@CoreColorToken +object DarkColorTokens { + val colorAlphaBlue100 = Color(0xff00055c) + val colorAlphaBlue1000 = Color(0xf062a0fe) + val colorAlphaBlue1100 = Color(0xf57cb2fd) + val colorAlphaBlue1200 = Color(0xf7a3c8ff) + val colorAlphaBlue1300 = Color(0xfccde1fe) + val colorAlphaBlue1400 = Color(0xffe6effe) + val colorAlphaBlue200 = Color(0xff00095c) + val colorAlphaBlue300 = Color(0xff001366) + val colorAlphaBlue400 = Color(0xff001e70) + val colorAlphaBlue500 = Color(0xa1003cbd) + val colorAlphaBlue600 = Color(0x87015afe) + val colorAlphaBlue700 = Color(0xa30665fe) + val colorAlphaBlue800 = Color(0xd61077fe) + val colorAlphaBlue900 = Color(0xeb4491fd) + val colorAlphaCyan100 = Color(0xff001142) + val colorAlphaCyan1000 = Color(0xe000bfe0) + val colorAlphaCyan1100 = Color(0xc926e7fd) + val colorAlphaCyan1200 = Color(0xd98af1ff) + val colorAlphaCyan1300 = Color(0xebc9f7fd) + val colorAlphaCyan1400 = Color(0xf5e1fbfe) + val colorAlphaCyan200 = Color(0xff001447) + val colorAlphaCyan300 = Color(0xff001b4d) + val colorAlphaCyan400 = Color(0xff00265c) + val colorAlphaCyan500 = Color(0xff003366) + val colorAlphaCyan600 = Color(0xff003f75) + val colorAlphaCyan700 = Color(0xff00538a) + val colorAlphaCyan800 = Color(0xe0007ebd) + val colorAlphaCyan900 = Color(0xff0091bd) + val colorAlphaFuchsia100 = Color(0xff28003d) + val colorAlphaFuchsia1000 = Color(0xd4f790fe) + val colorAlphaFuchsia1100 = Color(0xdbfaa4fe) + val colorAlphaFuchsia1200 = Color(0xe8fac3fe) + val colorAlphaFuchsia1300 = Color(0xf2fde0ff) + val colorAlphaFuchsia1400 = Color(0xfafdecfe) + val colorAlphaFuchsia200 = Color(0xff2d0042) + val colorAlphaFuchsia300 = Color(0xff36004d) + val colorAlphaFuchsia400 = Color(0xff45005c) + val colorAlphaFuchsia500 = Color(0x61ca0aff) + val colorAlphaFuchsia600 = Color(0x70d21fff) + val colorAlphaFuchsia700 = Color(0x8ad82ffe) + val colorAlphaFuchsia800 = Color(0xb5eb44fd) + val colorAlphaFuchsia900 = Color(0xccf172fd) + val colorAlphaGray100 = Color(0x05d8dbdf) + val colorAlphaGray1000 = Color(0x9ce1eefe) + val colorAlphaGray1100 = Color(0xade7f0fe) + val colorAlphaGray1200 = Color(0xc9edf4fc) + val colorAlphaGray1300 = Color(0xe3f2f7fd) + val colorAlphaGray1400 = Color(0xf2f6f9fe) + val colorAlphaGray200 = Color(0x0ad9c3df) + val colorAlphaGray300 = Color(0x0fe9dbf0) + val colorAlphaGray400 = Color(0x1aede7f4) + val colorAlphaGray500 = Color(0x26f4f7fa) + val colorAlphaGray600 = Color(0x33eceff8) + val colorAlphaGray700 = Color(0x45e7f1fd) + val colorAlphaGray800 = Color(0x69e0edff) + val colorAlphaGray900 = Color(0x8ae1effe) + val colorAlphaGreen100 = Color(0xff001f0c) + val colorAlphaGreen1000 = Color(0xa61bfebd) + val colorAlphaGreen1100 = Color(0xbd26fdbc) + val colorAlphaGreen1200 = Color(0xd486fdce) + val colorAlphaGreen1300 = Color(0xe8c4fde2) + val colorAlphaGreen1400 = Color(0xf5e2fdf1) + val colorAlphaGreen200 = Color(0xff001f0e) + val colorAlphaGreen300 = Color(0xff002412) + val colorAlphaGreen400 = Color(0xff002e1b) + val colorAlphaGreen500 = Color(0xff003d29) + val colorAlphaGreen600 = Color(0xff004732) + val colorAlphaGreen700 = Color(0xff005c45) + val colorAlphaGreen800 = Color(0xff007a62) + val colorAlphaGreen900 = Color(0x9412fdbe) + val colorAlphaLime100 = Color(0xff001a00) + val colorAlphaLime1000 = Color(0xa860fc2c) + val colorAlphaLime1100 = Color(0xbd71fd35) + val colorAlphaLime1200 = Color(0xd68dff5c) + val colorAlphaLime1300 = Color(0xebc3ffad) + val colorAlphaLime1400 = Color(0xf7e1fdd8) + val colorAlphaLime200 = Color(0xff001f00) + val colorAlphaLime300 = Color(0xff002900) + val colorAlphaLime400 = Color(0xff002e00) + val colorAlphaLime500 = Color(0xff003d00) + val colorAlphaLime600 = Color(0xff004d00) + val colorAlphaLime700 = Color(0xff005c00) + val colorAlphaLime800 = Color(0x732dfd0d) + val colorAlphaLime900 = Color(0x9454fd26) + val colorAlphaOrange100 = Color(0xff380000) + val colorAlphaOrange1000 = Color(0xebfe8310) + val colorAlphaOrange1100 = Color(0xf7fd953f) + val colorAlphaOrange1200 = Color(0xfcfdb781) + val colorAlphaOrange1300 = Color(0xffffd4b8) + val colorAlphaOrange1400 = Color(0xffffeadb) + val colorAlphaOrange200 = Color(0xff3d0000) + val colorAlphaOrange300 = Color(0xff470000) + val colorAlphaOrange400 = Color(0xff570000) + val colorAlphaOrange500 = Color(0xff700000) + val colorAlphaOrange600 = Color(0xff850400) + val colorAlphaOrange700 = Color(0xbdc72800) + val colorAlphaOrange800 = Color(0xb5ff5900) + val colorAlphaOrange900 = Color(0xd9fe740b) + val colorAlphaPink100 = Color(0xff38000f) + val colorAlphaPink1000 = Color(0xfaff6691) + val colorAlphaPink1100 = Color(0xfffe86a4) + val colorAlphaPink1200 = Color(0xffffadc0) + val colorAlphaPink1300 = Color(0xffffd1db) + val colorAlphaPink1400 = Color(0xffffebef) + val colorAlphaPink200 = Color(0xff3d0012) + val colorAlphaPink300 = Color(0xff470019) + val colorAlphaPink400 = Color(0xff570024) + val colorAlphaPink500 = Color(0xff6b0036) + val colorAlphaPink600 = Color(0x75fb0473) + val colorAlphaPink700 = Color(0x94fd1277) + val colorAlphaPink800 = Color(0xccfe1b79) + val colorAlphaPink900 = Color(0xf5fe4382) + val colorAlphaPurple100 = Color(0xff1a0057) + val colorAlphaPurple1000 = Color(0xfca28bfe) + val colorAlphaPurple1100 = Color(0xffab9afe) + val colorAlphaPurple1200 = Color(0xffc7bdff) + val colorAlphaPurple1300 = Color(0xffdfdbff) + val colorAlphaPurple1400 = Color(0xffeeebff) + val colorAlphaPurple200 = Color(0xff1d005c) + val colorAlphaPurple300 = Color(0xff22006b) + val colorAlphaPurple400 = Color(0xff2d0080) + val colorAlphaPurple500 = Color(0xff3d009e) + val colorAlphaPurple600 = Color(0xab690dfd) + val colorAlphaPurple700 = Color(0xc2712bfd) + val colorAlphaPurple800 = Color(0xeb7f4dff) + val colorAlphaPurple900 = Color(0xfa9271fe) + val colorAlphaRed100 = Color(0xff380000) + val colorAlphaRed1000 = Color(0xffff645c) + val colorAlphaRed1100 = Color(0xffff857a) + val colorAlphaRed1200 = Color(0xffffaea3) + val colorAlphaRed1300 = Color(0xffffd3cc) + val colorAlphaRed1400 = Color(0xffffe8e5) + val colorAlphaRed200 = Color(0xff3d0000) + val colorAlphaRed300 = Color(0xff470000) + val colorAlphaRed400 = Color(0xff5c0000) + val colorAlphaRed500 = Color(0xff700000) + val colorAlphaRed600 = Color(0xff850009) + val colorAlphaRed700 = Color(0x99fe0b24) + val colorAlphaRed800 = Color(0xcffe2530) + val colorAlphaRed900 = Color(0xfffd3d3a) + val colorAlphaYellow100 = Color(0xff380000) + val colorAlphaYellow1000 = Color(0xffcc8b00) + val colorAlphaYellow1100 = Color(0xffdba100) + val colorAlphaYellow1200 = Color(0xf0fdc50d) + val colorAlphaYellow1300 = Color(0xfffeda58) + val colorAlphaYellow1400 = Color(0xffffedb3) + val colorAlphaYellow200 = Color(0xff380300) + val colorAlphaYellow300 = Color(0xff420900) + val colorAlphaYellow400 = Color(0xff4d1400) + val colorAlphaYellow500 = Color(0xff5c2300) + val colorAlphaYellow600 = Color(0xde753300) + val colorAlphaYellow700 = Color(0xeb854200) + val colorAlphaYellow800 = Color(0xff9e5c00) + val colorAlphaYellow900 = Color(0xffbd7b00) + val colorBlue100 = Color(0xff00055a) + val colorBlue1000 = Color(0xff5e99f0) + val colorBlue1100 = Color(0xff7aacf4) + val colorBlue1200 = Color(0xffa1c4f8) + val colorBlue1300 = Color(0xffcbdffc) + val colorBlue1400 = Color(0xffe4eefe) + val colorBlue200 = Color(0xff00095d) + val colorBlue300 = Color(0xff001264) + val colorBlue400 = Color(0xff001e6f) + val colorBlue500 = Color(0xff062d80) + val colorBlue600 = Color(0xff083891) + val colorBlue700 = Color(0xff0b49ab) + val colorBlue800 = Color(0xff0e67d9) + val colorBlue900 = Color(0xff4187eb) + val colorCyan100 = Color(0xff001144) + val colorCyan1000 = Color(0xff02a7c6) + val colorCyan1100 = Color(0xff21bacd) + val colorCyan1200 = Color(0xff78d0dc) + val colorCyan1300 = Color(0xffb8e5eb) + val colorCyan1400 = Color(0xffdbf2f5) + val colorCyan200 = Color(0xff001448) + val colorCyan300 = Color(0xff001b4e) + val colorCyan400 = Color(0xff002559) + val colorCyan500 = Color(0xff003468) + val colorCyan600 = Color(0xff003f75) + val colorCyan700 = Color(0xff005188) + val colorCyan800 = Color(0xff0271aa) + val colorCyan900 = Color(0xff0093be) + val colorFuchsia100 = Color(0xff28003d) + val colorFuchsia1000 = Color(0xffcf78d7) + val colorFuchsia1100 = Color(0xffd991de) + val colorFuchsia1200 = Color(0xffe5b1e9) + val colorFuchsia1300 = Color(0xfff1d4f3) + val colorFuchsia1400 = Color(0xfff8e9f9) + val colorFuchsia200 = Color(0xff2e0044) + val colorFuchsia300 = Color(0xff37004e) + val colorFuchsia400 = Color(0xff46005e) + val colorFuchsia500 = Color(0xff560f6f) + val colorFuchsia600 = Color(0xff65177d) + val colorFuchsia700 = Color(0xff7d2394) + val colorFuchsia800 = Color(0xffaa36ba) + val colorFuchsia900 = Color(0xffc560cf) + val colorGray100 = Color(0xff14171b) + val colorGray1000 = Color(0xff9199a4) + val colorGray1100 = Color(0xffa3aab4) + val colorGray1200 = Color(0xffbdc3cc) + val colorGray1300 = Color(0xffd9dee4) + val colorGray1400 = Color(0xffebeef2) + val colorGray200 = Color(0xff181a1f) + val colorGray300 = Color(0xff1d1f24) + val colorGray400 = Color(0xff26282d) + val colorGray500 = Color(0xff323539) + val colorGray600 = Color(0xff3c3f44) + val colorGray700 = Color(0xff4a4f55) + val colorGray800 = Color(0xff656c76) + val colorGray900 = Color(0xff808994) + val colorGreen100 = Color(0xff001c0b) + val colorGreen1000 = Color(0xff17ac84) + val colorGreen1100 = Color(0xff1fc090) + val colorGreen1200 = Color(0xff72d5ae) + val colorGreen1300 = Color(0xffb5e8d1) + val colorGreen1400 = Color(0xffd9f4e7) + val colorGreen200 = Color(0xff001f0e) + val colorGreen300 = Color(0xff002513) + val colorGreen400 = Color(0xff002e1b) + val colorGreen500 = Color(0xff003d29) + val colorGreen600 = Color(0xff004832) + val colorGreen700 = Color(0xff005a43) + val colorGreen800 = Color(0xff007a62) + val colorGreen900 = Color(0xff129a78) + val colorLime100 = Color(0xff001b00) + val colorLime1000 = Color(0xff47ad26) + val colorLime1100 = Color(0xff56c02c) + val colorLime1200 = Color(0xff77d94f) + val colorLime1300 = Color(0xffb6eca3) + val colorLime1400 = Color(0xffdaf6d0) + val colorLime200 = Color(0xff002000) + val colorLime300 = Color(0xff002600) + val colorLime400 = Color(0xff003000) + val colorLime500 = Color(0xff003e00) + val colorLime600 = Color(0xff004a00) + val colorLime700 = Color(0xff005c00) + val colorLime800 = Color(0xff1d7c13) + val colorLime900 = Color(0xff389b20) + val colorOrange100 = Color(0xff380000) + val colorOrange1000 = Color(0xffeb7a12) + val colorOrange1100 = Color(0xfff6913d) + val colorOrange1200 = Color(0xfffbb37e) + val colorOrange1300 = Color(0xffffd5b9) + val colorOrange1400 = Color(0xffffeadb) + val colorOrange200 = Color(0xff3c0000) + val colorOrange300 = Color(0xff470000) + val colorOrange400 = Color(0xff580000) + val colorOrange500 = Color(0xff710000) + val colorOrange600 = Color(0xff830500) + val colorOrange700 = Color(0xff972206) + val colorOrange800 = Color(0xffb94607) + val colorOrange900 = Color(0xffda670d) + val colorPink100 = Color(0xff37000f) + val colorPink1000 = Color(0xfffa658f) + val colorPink1100 = Color(0xfffe84a2) + val colorPink1200 = Color(0xffffabbe) + val colorPink1300 = Color(0xffffd2dc) + val colorPink1400 = Color(0xffffe8ed) + val colorPink200 = Color(0xff3c0012) + val colorPink300 = Color(0xff450018) + val colorPink400 = Color(0xff550024) + val colorPink500 = Color(0xff6d0036) + val colorPink600 = Color(0xff7c0c41) + val colorPink700 = Color(0xff99114f) + val colorPink800 = Color(0xffce1865) + val colorPink900 = Color(0xfff4427d) + val colorPurple100 = Color(0xff1a0055) + val colorPurple1000 = Color(0xff9e87fc) + val colorPurple1100 = Color(0xffad9cfe) + val colorPurple1200 = Color(0xffc4baff) + val colorPurple1300 = Color(0xffdedaff) + val colorPurple1400 = Color(0xffeeebff) + val colorPurple200 = Color(0xff1c005a) + val colorPurple300 = Color(0xff22006a) + val colorPurple400 = Color(0xff2c0080) + val colorPurple500 = Color(0xff3d009e) + val colorPurple600 = Color(0xff4a0db1) + val colorPurple700 = Color(0xff5a27c6) + val colorPurple800 = Color(0xff7849ec) + val colorPurple900 = Color(0xff9171f9) + val colorRed100 = Color(0xff370000) + val colorRed1000 = Color(0xffff665d) + val colorRed1100 = Color(0xffff877c) + val colorRed1200 = Color(0xffffaea4) + val colorRed1300 = Color(0xffffd4cd) + val colorRed1400 = Color(0xffffe9e6) + val colorRed200 = Color(0xff3e0000) + val colorRed300 = Color(0xff470000) + val colorRed400 = Color(0xff590000) + val colorRed500 = Color(0xff710000) + val colorRed600 = Color(0xff830009) + val colorRed700 = Color(0xff9f0d1e) + val colorRed800 = Color(0xffd1212a) + val colorRed900 = Color(0xfffd3e3c) + val colorThemeBg = Color(0xff101317) + val colorTransparent = Color(0x00000000) + val colorYellow100 = Color(0xff360000) + val colorYellow1000 = Color(0xffcc8c00) + val colorYellow1100 = Color(0xffdb9f00) + val colorYellow1200 = Color(0xffefbb0b) + val colorYellow1300 = Color(0xfffedb58) + val colorYellow1400 = Color(0xffffedb1) + val colorYellow200 = Color(0xff3a0300) + val colorYellow300 = Color(0xff410900) + val colorYellow400 = Color(0xff4c1400) + val colorYellow500 = Color(0xff5c2400) + val colorYellow600 = Color(0xff682e03) + val colorYellow700 = Color(0xff7c3e02) + val colorYellow800 = Color(0xff9d5b00) + val colorYellow900 = Color(0xffbc7a00) +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/internal/DarkHcColorTokens.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/internal/DarkHcColorTokens.kt new file mode 100644 index 0000000000..31406f6ffc --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/internal/DarkHcColorTokens.kt @@ -0,0 +1,335 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +/** + * !!! WARNING !!! + * + * THIS IS AN AUTOGENERATED FILE. + * DO NOT EDIT MANUALLY. + */ + + + +@file:Suppress("all") +package io.element.android.compound.tokens.generated.internal + +import androidx.compose.ui.graphics.Color +import io.element.android.compound.annotations.CoreColorToken + +@CoreColorToken +object DarkHcColorTokens { + val colorAlphaBlue100 = Color(0xff00095c) + val colorAlphaBlue1000 = Color(0xf79ec5ff) + val colorAlphaBlue1100 = Color(0xfab8d4ff) + val colorAlphaBlue1200 = Color(0xfcc8defe) + val colorAlphaBlue1300 = Color(0xffe6effe) + val colorAlphaBlue1400 = Color(0xfff1f6fe) + val colorAlphaBlue200 = Color(0xff001366) + val colorAlphaBlue300 = Color(0xff001e70) + val colorAlphaBlue400 = Color(0xd1002b8f) + val colorAlphaBlue500 = Color(0x87015afe) + val colorAlphaBlue600 = Color(0xa30665fe) + val colorAlphaBlue700 = Color(0xcf0d71fd) + val colorAlphaBlue800 = Color(0xe83488fe) + val colorAlphaBlue900 = Color(0xf78bb9fd) + val colorAlphaCyan100 = Color(0xff001447) + val colorAlphaCyan1000 = Color(0xd67beffe) + val colorAlphaCyan1100 = Color(0xe0a4f4fe) + val colorAlphaCyan1200 = Color(0xe8bef5fe) + val colorAlphaCyan1300 = Color(0xf5e1fbfe) + val colorAlphaCyan1400 = Color(0xfaf1fdfe) + val colorAlphaCyan200 = Color(0xff001b4d) + val colorAlphaCyan300 = Color(0xff00265c) + val colorAlphaCyan400 = Color(0xff002d61) + val colorAlphaCyan500 = Color(0xff003f75) + val colorAlphaCyan600 = Color(0xff00538a) + val colorAlphaCyan700 = Color(0xff006da3) + val colorAlphaCyan800 = Color(0xff008ebd) + val colorAlphaCyan900 = Color(0xcf52edfe) + val colorAlphaFuchsia100 = Color(0xff2d0042) + val colorAlphaFuchsia1000 = Color(0xe6fabefe) + val colorAlphaFuchsia1100 = Color(0xedfacefd) + val colorAlphaFuchsia1200 = Color(0xf2fcd7fe) + val colorAlphaFuchsia1300 = Color(0xfafdecfe) + val colorAlphaFuchsia1400 = Color(0xfcfdf2fd) + val colorAlphaFuchsia200 = Color(0xff36004d) + val colorAlphaFuchsia300 = Color(0xff45005c) + val colorAlphaFuchsia400 = Color(0xd95a0075) + val colorAlphaFuchsia500 = Color(0x70d21fff) + val colorAlphaFuchsia600 = Color(0x8ad82ffe) + val colorAlphaFuchsia700 = Color(0xade640fc) + val colorAlphaFuchsia800 = Color(0xc7f467fe) + val colorAlphaFuchsia900 = Color(0xe0f9b3ff) + val colorAlphaGray100 = Color(0x0ad9c3df) + val colorAlphaGray1000 = Color(0xc2f0f7ff) + val colorAlphaGray1100 = Color(0xd1f0f7ff) + val colorAlphaGray1200 = Color(0xe0f1f6fd) + val colorAlphaGray1300 = Color(0xf2f6f9fe) + val colorAlphaGray1400 = Color(0xf7fbfdfe) + val colorAlphaGray200 = Color(0x0fe9dbf0) + val colorAlphaGray300 = Color(0x1aede7f4) + val colorAlphaGray400 = Color(0x21e1e4ef) + val colorAlphaGray500 = Color(0x33eceff8) + val colorAlphaGray600 = Color(0x45e7f1fd) + val colorAlphaGray700 = Color(0x63dfebfb) + val colorAlphaGray800 = Color(0x82dceafe) + val colorAlphaGray900 = Color(0xb8ecf4fe) + val colorAlphaGreen100 = Color(0xff001f0e) + val colorAlphaGreen1000 = Color(0xcf75ffc8) + val colorAlphaGreen1100 = Color(0xdba4fed7) + val colorAlphaGreen1200 = Color(0xe6bffde1) + val colorAlphaGreen1300 = Color(0xf5e2fdf1) + val colorAlphaGreen1400 = Color(0xfaedfdf5) + val colorAlphaGreen200 = Color(0xff002412) + val colorAlphaGreen300 = Color(0xff002e1b) + val colorAlphaGreen400 = Color(0xff003824) + val colorAlphaGreen500 = Color(0xff004732) + val colorAlphaGreen600 = Color(0xff005c45) + val colorAlphaGreen700 = Color(0xff00755e) + val colorAlphaGreen800 = Color(0x8a12fdc2) + val colorAlphaGreen900 = Color(0xc740fcba) + val colorAlphaLime100 = Color(0xff001f00) + val colorAlphaLime1000 = Color(0xd47bfe3e) + val colorAlphaLime1100 = Color(0xe0a4fd81) + val colorAlphaLime1200 = Color(0xe8c1fea9) + val colorAlphaLime1300 = Color(0xf7e1fdd8) + val colorAlphaLime1400 = Color(0xfaedfee7) + val colorAlphaLime200 = Color(0xff002900) + val colorAlphaLime300 = Color(0xff002e00) + val colorAlphaLime400 = Color(0xff003800) + val colorAlphaLime500 = Color(0xff004d00) + val colorAlphaLime600 = Color(0xff005c00) + val colorAlphaLime700 = Color(0x6b23ff0a) + val colorAlphaLime800 = Color(0x8c4dfe25) + val colorAlphaLime900 = Color(0xc774fe34) + val colorAlphaOrange100 = Color(0xff3d0000) + val colorAlphaOrange1000 = Color(0xfaffb175) + val colorAlphaOrange1100 = Color(0xfffdc196) + val colorAlphaOrange1200 = Color(0xfffed1b3) + val colorAlphaOrange1300 = Color(0xffffeadb) + val colorAlphaOrange1400 = Color(0xfffff2eb) + val colorAlphaOrange200 = Color(0xff470000) + val colorAlphaOrange300 = Color(0xff570000) + val colorAlphaOrange400 = Color(0xff660000) + val colorAlphaOrange500 = Color(0xff850400) + val colorAlphaOrange600 = Color(0xbdc72800) + val colorAlphaOrange700 = Color(0xb3fa5300) + val colorAlphaOrange800 = Color(0xcffe7206) + val colorAlphaOrange900 = Color(0xfafda058) + val colorAlphaPink100 = Color(0xff3d0012) + val colorAlphaPink1000 = Color(0xffffa3b9) + val colorAlphaPink1100 = Color(0xffffbdcb) + val colorAlphaPink1200 = Color(0xffffccd7) + val colorAlphaPink1300 = Color(0xffffebef) + val colorAlphaPink1400 = Color(0xfffff0f3) + val colorAlphaPink200 = Color(0xff470019) + val colorAlphaPink300 = Color(0xff570024) + val colorAlphaPink400 = Color(0xff61002d) + val colorAlphaPink500 = Color(0x75fb0473) + val colorAlphaPink600 = Color(0x94fd1277) + val colorAlphaPink700 = Color(0xc2fe1b79) + val colorAlphaPink800 = Color(0xf2fd2b78) + val colorAlphaPink900 = Color(0xffff94ad) + val colorAlphaPurple100 = Color(0xff1d005c) + val colorAlphaPurple1000 = Color(0xffc2b8ff) + val colorAlphaPurple1100 = Color(0xffcec7ff) + val colorAlphaPurple1200 = Color(0xffdbd6ff) + val colorAlphaPurple1300 = Color(0xffeeebff) + val colorAlphaPurple1400 = Color(0xfff6f5ff) + val colorAlphaPurple200 = Color(0xff22006b) + val colorAlphaPurple300 = Color(0xff2d0080) + val colorAlphaPurple400 = Color(0xff34008f) + val colorAlphaPurple500 = Color(0xab690dfd) + val colorAlphaPurple600 = Color(0xc2712bfd) + val colorAlphaPurple700 = Color(0xe67f49fd) + val colorAlphaPurple800 = Color(0xf7906bff) + val colorAlphaPurple900 = Color(0xffb7a8ff) + val colorAlphaRed100 = Color(0xff3d0000) + val colorAlphaRed1000 = Color(0xffffa89e) + val colorAlphaRed1100 = Color(0xffffbfb8) + val colorAlphaRed1200 = Color(0xffffcec7) + val colorAlphaRed1300 = Color(0xffffe8e5) + val colorAlphaRed1400 = Color(0xfffff3f0) + val colorAlphaRed200 = Color(0xff470000) + val colorAlphaRed300 = Color(0xff5c0000) + val colorAlphaRed400 = Color(0xff660000) + val colorAlphaRed500 = Color(0xff850009) + val colorAlphaRed600 = Color(0x99fe0b24) + val colorAlphaRed700 = Color(0xc4ff242f) + val colorAlphaRed800 = Color(0xf5ff2e31) + val colorAlphaRed900 = Color(0xffff988f) + val colorAlphaYellow100 = Color(0xff380300) + val colorAlphaYellow1000 = Color(0xebfec406) + val colorAlphaYellow1100 = Color(0xf7fecf16) + val colorAlphaYellow1200 = Color(0xfffed634) + val colorAlphaYellow1300 = Color(0xffffedb3) + val colorAlphaYellow1400 = Color(0xfffff4d1) + val colorAlphaYellow200 = Color(0xff420900) + val colorAlphaYellow300 = Color(0xff4d1400) + val colorAlphaYellow400 = Color(0xff571e00) + val colorAlphaYellow500 = Color(0xde753300) + val colorAlphaYellow600 = Color(0xeb854200) + val colorAlphaYellow700 = Color(0xff995700) + val colorAlphaYellow800 = Color(0xffb37100) + val colorAlphaYellow900 = Color(0xffe6ac00) + val colorBlue100 = Color(0xff00095d) + val colorBlue1000 = Color(0xff9ac0f8) + val colorBlue1100 = Color(0xffb2cffa) + val colorBlue1200 = Color(0xffc5dbfc) + val colorBlue1300 = Color(0xffe4eefe) + val colorBlue1400 = Color(0xffeff5fe) + val colorBlue200 = Color(0xff001264) + val colorBlue300 = Color(0xff001e6f) + val colorBlue400 = Color(0xff032677) + val colorBlue500 = Color(0xff083891) + val colorBlue600 = Color(0xff0b49ab) + val colorBlue700 = Color(0xff0e61d1) + val colorBlue800 = Color(0xff337fe9) + val colorBlue900 = Color(0xff89b5f6) + val colorCyan100 = Color(0xff001448) + val colorCyan1000 = Color(0xff6bccd9) + val colorCyan1100 = Color(0xff93d9e2) + val colorCyan1200 = Color(0xffafe2e9) + val colorCyan1300 = Color(0xffdbf2f5) + val colorCyan1400 = Color(0xffeaf7f9) + val colorCyan200 = Color(0xff001b4e) + val colorCyan300 = Color(0xff002559) + val colorCyan400 = Color(0xff002d61) + val colorCyan500 = Color(0xff003f75) + val colorCyan600 = Color(0xff005188) + val colorCyan700 = Color(0xff006ca4) + val colorCyan800 = Color(0xff008aba) + val colorCyan900 = Color(0xff46c3d2) + val colorFuchsia100 = Color(0xff2e0044) + val colorFuchsia1000 = Color(0xffe3abe7) + val colorFuchsia1100 = Color(0xffeac0ed) + val colorFuchsia1200 = Color(0xfff0cff2) + val colorFuchsia1300 = Color(0xfff8e9f9) + val colorFuchsia1400 = Color(0xfffbf1fb) + val colorFuchsia200 = Color(0xff37004e) + val colorFuchsia300 = Color(0xff46005e) + val colorFuchsia400 = Color(0xff4f0368) + val colorFuchsia500 = Color(0xff65177d) + val colorFuchsia600 = Color(0xff7d2394) + val colorFuchsia700 = Color(0xffa233b3) + val colorFuchsia800 = Color(0xffc153cb) + val colorFuchsia900 = Color(0xffdd9de3) + val colorGray100 = Color(0xff181a1f) + val colorGray1000 = Color(0xffb8bfc7) + val colorGray1100 = Color(0xffc8ced5) + val colorGray1200 = Color(0xffd5dae1) + val colorGray1300 = Color(0xffebeef2) + val colorGray1400 = Color(0xfff2f5f7) + val colorGray200 = Color(0xff1d1f24) + val colorGray300 = Color(0xff26282d) + val colorGray400 = Color(0xff2b2e33) + val colorGray500 = Color(0xff3c3f44) + val colorGray600 = Color(0xff4a4f55) + val colorGray700 = Color(0xff606770) + val colorGray800 = Color(0xff79818d) + val colorGray900 = Color(0xffacb4bd) + val colorGreen100 = Color(0xff001f0e) + val colorGreen1000 = Color(0xff61d2a6) + val colorGreen1100 = Color(0xff8fddbc) + val colorGreen1200 = Color(0xfface6cc) + val colorGreen1300 = Color(0xffd9f4e7) + val colorGreen1400 = Color(0xffe9f8f1) + val colorGreen200 = Color(0xff002513) + val colorGreen300 = Color(0xff002e1b) + val colorGreen400 = Color(0xff003622) + val colorGreen500 = Color(0xff004832) + val colorGreen600 = Color(0xff005a43) + val colorGreen700 = Color(0xff00745c) + val colorGreen800 = Color(0xff109173) + val colorGreen900 = Color(0xff37c998) + val colorLime100 = Color(0xff002000) + val colorLime1000 = Color(0xff6ad639) + val colorLime1100 = Color(0xff92e175) + val colorLime1200 = Color(0xffafe99a) + val colorLime1300 = Color(0xffdaf6d0) + val colorLime1400 = Color(0xffe9f9e3) + val colorLime200 = Color(0xff002600) + val colorLime300 = Color(0xff003000) + val colorLime400 = Color(0xff003700) + val colorLime500 = Color(0xff004a00) + val colorLime600 = Color(0xff005c00) + val colorLime700 = Color(0xff187611) + val colorLime800 = Color(0xff31941d) + val colorLime900 = Color(0xff5eca2f) + val colorOrange100 = Color(0xff3c0000) + val colorOrange1000 = Color(0xfffaad73) + val colorOrange1100 = Color(0xfffdc197) + val colorOrange1200 = Color(0xfffed0b1) + val colorOrange1300 = Color(0xffffeadb) + val colorOrange1400 = Color(0xfffff2ea) + val colorOrange200 = Color(0xff470000) + val colorOrange300 = Color(0xff580000) + val colorOrange400 = Color(0xff650000) + val colorOrange500 = Color(0xff830500) + val colorOrange600 = Color(0xff972206) + val colorOrange700 = Color(0xffb44007) + val colorOrange800 = Color(0xffd15f0b) + val colorOrange900 = Color(0xfff89d58) + val colorPink100 = Color(0xff3c0012) + val colorPink1000 = Color(0xffffa4b9) + val colorPink1100 = Color(0xffffbbca) + val colorPink1200 = Color(0xffffccd7) + val colorPink1300 = Color(0xffffe8ed) + val colorPink1400 = Color(0xfffff1f4) + val colorPink200 = Color(0xff450018) + val colorPink300 = Color(0xff550024) + val colorPink400 = Color(0xff61002d) + val colorPink500 = Color(0xff7c0c41) + val colorPink600 = Color(0xff99114f) + val colorPink700 = Color(0xffc51761) + val colorPink800 = Color(0xfff12c75) + val colorPink900 = Color(0xffff92ac) + val colorPurple100 = Color(0xff1c005a) + val colorPurple1000 = Color(0xffc0b5ff) + val colorPurple1100 = Color(0xffcec7ff) + val colorPurple1200 = Color(0xffdad5ff) + val colorPurple1300 = Color(0xffeeebff) + val colorPurple1400 = Color(0xfff5f3ff) + val colorPurple200 = Color(0xff22006a) + val colorPurple300 = Color(0xff2c0080) + val colorPurple400 = Color(0xff350090) + val colorPurple500 = Color(0xff4a0db1) + val colorPurple600 = Color(0xff5a27c6) + val colorPurple700 = Color(0xff7343e6) + val colorPurple800 = Color(0xff8b66f8) + val colorPurple900 = Color(0xffb6a7ff) + val colorRed100 = Color(0xff3e0000) + val colorRed1000 = Color(0xffffa79d) + val colorRed1100 = Color(0xffffbdb5) + val colorRed1200 = Color(0xffffcfc8) + val colorRed1300 = Color(0xffffe9e6) + val colorRed1400 = Color(0xfffff2ef) + val colorRed200 = Color(0xff470000) + val colorRed300 = Color(0xff590000) + val colorRed400 = Color(0xff640000) + val colorRed500 = Color(0xff830009) + val colorRed600 = Color(0xff9f0d1e) + val colorRed700 = Color(0xffc81e28) + val colorRed800 = Color(0xfff52f33) + val colorRed900 = Color(0xffff968c) + val colorThemeBg = Color(0xff101317) + val colorTransparent = Color(0x00000000) + val colorYellow100 = Color(0xff3a0300) + val colorYellow1000 = Color(0xffebb607) + val colorYellow1100 = Color(0xfff7c816) + val colorYellow1200 = Color(0xfffed632) + val colorYellow1300 = Color(0xffffedb1) + val colorYellow1400 = Color(0xfffff4d0) + val colorYellow200 = Color(0xff410900) + val colorYellow300 = Color(0xff4c1400) + val colorYellow400 = Color(0xff541d00) + val colorYellow500 = Color(0xff682e03) + val colorYellow600 = Color(0xff7c3e02) + val colorYellow700 = Color(0xff985600) + val colorYellow800 = Color(0xffb47200) + val colorYellow900 = Color(0xffe3aa00) +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/internal/LightColorTokens.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/internal/LightColorTokens.kt new file mode 100644 index 0000000000..119db9dade --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/internal/LightColorTokens.kt @@ -0,0 +1,335 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +/** + * !!! WARNING !!! + * + * THIS IS AN AUTOGENERATED FILE. + * DO NOT EDIT MANUALLY. + */ + + + +@file:Suppress("all") +package io.element.android.compound.tokens.generated.internal + +import androidx.compose.ui.graphics.Color +import io.element.android.compound.annotations.CoreColorToken + +@CoreColorToken +object LightColorTokens { + val colorAlphaBlue100 = Color(0x08389cff) + val colorAlphaBlue1000 = Color(0xfc0256c5) + val colorAlphaBlue1100 = Color(0xfa0148b2) + val colorAlphaBlue1200 = Color(0xfc013693) + val colorAlphaBlue1300 = Color(0xff012579) + val colorAlphaBlue1400 = Color(0xff000e66) + val colorAlphaBlue200 = Color(0x0d2474ff) + val colorAlphaBlue300 = Color(0x170a70ff) + val colorAlphaBlue400 = Color(0x290b6af9) + val colorAlphaBlue500 = Color(0x47096cf6) + val colorAlphaBlue600 = Color(0x5e0663ef) + val colorAlphaBlue700 = Color(0x820264ed) + val colorAlphaBlue800 = Color(0xbf0062eb) + val colorAlphaBlue900 = Color(0xfc0165df) + val colorAlphaCyan100 = Color(0x0816bbbb) + val colorAlphaCyan1000 = Color(0xff00649e) + val colorAlphaCyan1100 = Color(0xff00568f) + val colorAlphaCyan1200 = Color(0xff003f75) + val colorAlphaCyan1300 = Color(0xff002c61) + val colorAlphaCyan1400 = Color(0xff001a52) + val colorAlphaCyan200 = Color(0x0f16abbb) + val colorAlphaCyan300 = Color(0x1c00a8c2) + val colorAlphaCyan400 = Color(0x3800aabd) + val colorAlphaCyan500 = Color(0x6605abbd) + val colorAlphaCyan600 = Color(0x8a01aac1) + val colorAlphaCyan700 = Color(0xeb01b7cb) + val colorAlphaCyan800 = Color(0xff0095c2) + val colorAlphaCyan900 = Color(0xff0074ad) + val colorAlphaFuchsia100 = Color(0x05cc05cc) + val colorAlphaFuchsia1000 = Color(0xd6820198) + val colorAlphaFuchsia1100 = Color(0xe073038c) + val colorAlphaFuchsia1200 = Color(0xed5d0279) + val colorAlphaFuchsia1300 = Color(0xff4d0066) + val colorAlphaFuchsia1400 = Color(0xff34004d) + val colorAlphaFuchsia200 = Color(0x0ab505cc) + val colorAlphaFuchsia300 = Color(0x12b60cc6) + val colorAlphaFuchsia400 = Color(0x21bd09c3) + val colorAlphaFuchsia500 = Color(0x3bb407c0) + val colorAlphaFuchsia600 = Color(0x4fb207bb) + val colorAlphaFuchsia700 = Color(0x6eaa04b9) + val colorAlphaFuchsia800 = Color(0xa3ab03ba) + val colorAlphaFuchsia900 = Color(0xcc9900ad) + val colorAlphaGray100 = Color(0x0536699b) + val colorAlphaGray1000 = Color(0xa8030c1b) + val colorAlphaGray1100 = Color(0xb5030b16) + val colorAlphaGray1200 = Color(0xc402070d) + val colorAlphaGray1300 = Color(0xd603050c) + val colorAlphaGray1400 = Color(0xe6020408) + val colorAlphaGray200 = Color(0x0a366881) + val colorAlphaGray300 = Color(0x0f052657) + val colorAlphaGray400 = Color(0x1f052e61) + val colorAlphaGray500 = Color(0x33052448) + val colorAlphaGray600 = Color(0x42011d3c) + val colorAlphaGray700 = Color(0x59011532) + val colorAlphaGray800 = Color(0x8003152b) + val colorAlphaGray900 = Color(0x9c031021) + val colorAlphaGreen100 = Color(0x0816bb79) + val colorAlphaGreen1000 = Color(0xff006b52) + val colorAlphaGreen1100 = Color(0xff005c45) + val colorAlphaGreen1200 = Color(0xff004732) + val colorAlphaGreen1300 = Color(0xff00331f) + val colorAlphaGreen1400 = Color(0xff002411) + val colorAlphaGreen200 = Color(0x0f16bb69) + val colorAlphaGreen300 = Color(0x1c00b85c) + val colorAlphaGreen400 = Color(0x3b07b661) + val colorAlphaGreen500 = Color(0x6904b96a) + val colorAlphaGreen600 = Color(0x8f01b76e) + val colorAlphaGreen700 = Color(0xf501c18a) + val colorAlphaGreen800 = Color(0xff009975) + val colorAlphaGreen900 = Color(0xff007a62) + val colorAlphaLime100 = Color(0x0a4fcd1d) + val colorAlphaLime1000 = Color(0xff007000) + val colorAlphaLime1100 = Color(0xff006100) + val colorAlphaLime1200 = Color(0xff004d00) + val colorAlphaLime1300 = Color(0xff003800) + val colorAlphaLime1400 = Color(0xff002400) + val colorAlphaLime200 = Color(0x1238d40c) + val colorAlphaLime300 = Color(0x262ecf02) + val colorAlphaLime400 = Color(0x473ace09) + val colorAlphaLime500 = Color(0x8237ca02) + val colorAlphaLime600 = Color(0xb540ce03) + val colorAlphaLime700 = Color(0xdb39bd00) + val colorAlphaLime800 = Color(0xe8209301) + val colorAlphaLime900 = Color(0xf5107902) + val colorAlphaOrange100 = Color(0x0aff8138) + val colorAlphaOrange1000 = Color(0xffad3400) + val colorAlphaOrange1100 = Color(0xff992100) + val colorAlphaOrange1200 = Color(0xff850000) + val colorAlphaOrange1300 = Color(0xff610000) + val colorAlphaOrange1400 = Color(0xff470000) + val colorAlphaOrange200 = Color(0x12ff7d1a) + val colorAlphaOrange300 = Color(0x1cff6c0a) + val colorAlphaOrange400 = Color(0x38ff6d05) + val colorAlphaOrange500 = Color(0x5eff6a00) + val colorAlphaOrange600 = Color(0x85fc6f03) + val colorAlphaOrange700 = Color(0xbff56e00) + val colorAlphaOrange800 = Color(0xffdb6600) + val colorAlphaOrange900 = Color(0xffbd4500) + val colorAlphaPink100 = Color(0x05ff0537) + val colorAlphaPink1000 = Color(0xf7b60256) + val colorAlphaPink1100 = Color(0xf79e004c) + val colorAlphaPink1200 = Color(0xfa79013d) + val colorAlphaPink1300 = Color(0xff61002c) + val colorAlphaPink1400 = Color(0xff420017) + val colorAlphaPink200 = Color(0x0aff0537) + val colorAlphaPink300 = Color(0x14ff1447) + val colorAlphaPink400 = Color(0x21ff0037) + val colorAlphaPink500 = Color(0x3dff0037) + val colorAlphaPink600 = Color(0x54ff053f) + val colorAlphaPink700 = Color(0x78ff0040) + val colorAlphaPink800 = Color(0xbff50052) + val colorAlphaPink900 = Color(0xf5cf025e) + val colorAlphaPurple100 = Color(0x053838ff) + val colorAlphaPurple1000 = Color(0xc94502d4) + val colorAlphaPurple1100 = Color(0xdb4303c4) + val colorAlphaPurple1200 = Color(0xfc4a02b6) + val colorAlphaPurple1300 = Color(0xff34008f) + val colorAlphaPurple1400 = Color(0xff200066) + val colorAlphaPurple200 = Color(0x0a5338ff) + val colorAlphaPurple300 = Color(0x12381aff) + val colorAlphaPurple400 = Color(0x1f2f0fff) + val colorAlphaPurple500 = Color(0x332605ff) + val colorAlphaPurple600 = Color(0x452b05ff) + val colorAlphaPurple700 = Color(0x613305ff) + val colorAlphaPurple800 = Color(0x8f3b01f9) + val colorAlphaPurple900 = Color(0xba4902ed) + val colorAlphaRed100 = Color(0x08ff5938) + val colorAlphaRed1000 = Color(0xf2bb0217) + val colorAlphaRed1100 = Color(0xfca2011c) + val colorAlphaRed1200 = Color(0xff850007) + val colorAlphaRed1300 = Color(0xff610000) + val colorAlphaRed1400 = Color(0xff470000) + val colorAlphaRed200 = Color(0x0aff391f) + val colorAlphaRed300 = Color(0x14ff3814) + val colorAlphaRed400 = Color(0x26ff2b0a) + val colorAlphaRed500 = Color(0x45ff2605) + val colorAlphaRed600 = Color(0x5cff2205) + val colorAlphaRed700 = Color(0x80ff1a05) + val colorAlphaRed800 = Color(0xc4ff0505) + val colorAlphaRed900 = Color(0xe8cf0213) + val colorAlphaYellow100 = Color(0x0fffcd05) + val colorAlphaYellow1000 = Color(0xff8f4c00) + val colorAlphaYellow1100 = Color(0xff804000) + val colorAlphaYellow1200 = Color(0xff6b2e00) + val colorAlphaYellow1300 = Color(0xff571b00) + val colorAlphaYellow1400 = Color(0xff420700) + val colorAlphaYellow200 = Color(0x21ffc70f) + val colorAlphaYellow300 = Color(0x40ffc905) + val colorAlphaYellow400 = Color(0x7dffc905) + val colorAlphaYellow500 = Color(0xfffacc00) + val colorAlphaYellow600 = Color(0xfff0bc00) + val colorAlphaYellow700 = Color(0xffe0a500) + val colorAlphaYellow800 = Color(0xffbd7b00) + val colorAlphaYellow900 = Color(0xff9e5a00) + val colorBlue100 = Color(0xfff9fcff) + val colorBlue1000 = Color(0xff0558c7) + val colorBlue1100 = Color(0xff064ab1) + val colorBlue1200 = Color(0xff043894) + val colorBlue1300 = Color(0xff012478) + val colorBlue1400 = Color(0xff000e65) + val colorBlue200 = Color(0xfff4f8ff) + val colorBlue300 = Color(0xffe9f2ff) + val colorBlue400 = Color(0xffd8e7fe) + val colorBlue500 = Color(0xffbad5fc) + val colorBlue600 = Color(0xffa3c6fa) + val colorBlue700 = Color(0xff7eaff6) + val colorBlue800 = Color(0xff4088ee) + val colorBlue900 = Color(0xff0467dd) + val colorCyan100 = Color(0xfff8fdfd) + val colorCyan1000 = Color(0xff00629c) + val colorCyan1100 = Color(0xff00548c) + val colorCyan1200 = Color(0xff004077) + val colorCyan1300 = Color(0xff002b61) + val colorCyan1400 = Color(0xff00194f) + val colorCyan200 = Color(0xfff1fafb) + val colorCyan300 = Color(0xffe3f5f8) + val colorCyan400 = Color(0xffc7ecf0) + val colorCyan500 = Color(0xff9bdde5) + val colorCyan600 = Color(0xff76d1dd) + val colorCyan700 = Color(0xff15becf) + val colorCyan800 = Color(0xff0094c0) + val colorCyan900 = Color(0xff0072ac) + val colorFuchsia100 = Color(0xfffefafe) + val colorFuchsia1000 = Color(0xff972aaa) + val colorFuchsia1100 = Color(0xff822198) + val colorFuchsia1200 = Color(0xff671481) + val colorFuchsia1300 = Color(0xff4e0068) + val colorFuchsia1400 = Color(0xff34004c) + val colorFuchsia200 = Color(0xfffcf5fd) + val colorFuchsia300 = Color(0xfffaeefb) + val colorFuchsia400 = Color(0xfff6dff7) + val colorFuchsia500 = Color(0xffedc6f0) + val colorFuchsia600 = Color(0xffe7b2ea) + val colorFuchsia700 = Color(0xffdb93e1) + val colorFuchsia800 = Color(0xffc85ed1) + val colorFuchsia900 = Color(0xffad33bd) + val colorGray100 = Color(0xfffbfcfd) + val colorGray1000 = Color(0xff595e67) + val colorGray1100 = Color(0xff4c5158) + val colorGray1200 = Color(0xff3c4045) + val colorGray1300 = Color(0xff2b2d32) + val colorGray1400 = Color(0xff1b1d22) + val colorGray200 = Color(0xfff7f9fa) + val colorGray300 = Color(0xfff0f2f5) + val colorGray400 = Color(0xffe1e6ec) + val colorGray500 = Color(0xffcdd3da) + val colorGray600 = Color(0xffbdc4cc) + val colorGray700 = Color(0xffa6adb7) + val colorGray800 = Color(0xff818a95) + val colorGray900 = Color(0xff656d77) + val colorGreen100 = Color(0xfff8fdfb) + val colorGreen1000 = Color(0xff006b52) + val colorGreen1100 = Color(0xff005c45) + val colorGreen1200 = Color(0xff004933) + val colorGreen1300 = Color(0xff003420) + val colorGreen1400 = Color(0xff002311) + val colorGreen200 = Color(0xfff1fbf6) + val colorGreen300 = Color(0xffe3f7ed) + val colorGreen400 = Color(0xffc6eedb) + val colorGreen500 = Color(0xff98e1c1) + val colorGreen600 = Color(0xff71d7ae) + val colorGreen700 = Color(0xff0bc491) + val colorGreen800 = Color(0xff009b78) + val colorGreen900 = Color(0xff007a61) + val colorLime100 = Color(0xfff8fdf6) + val colorLime1000 = Color(0xff006e00) + val colorLime1100 = Color(0xff005f00) + val colorLime1200 = Color(0xff004b00) + val colorLime1300 = Color(0xff003600) + val colorLime1400 = Color(0xff002400) + val colorLime200 = Color(0xfff1fcee) + val colorLime300 = Color(0xffe0f8d9) + val colorLime400 = Color(0xffc8f1ba) + val colorLime500 = Color(0xff99e57e) + val colorLime600 = Color(0xff76db4c) + val colorLime700 = Color(0xff54c424) + val colorLime800 = Color(0xff359d18) + val colorLime900 = Color(0xff197d0c) + val colorOrange100 = Color(0xfffffaf7) + val colorOrange1000 = Color(0xffac3300) + val colorOrange1100 = Color(0xff9b2200) + val colorOrange1200 = Color(0xff850000) + val colorOrange1300 = Color(0xff620000) + val colorOrange1400 = Color(0xff450000) + val colorOrange200 = Color(0xfffff6ef) + val colorOrange300 = Color(0xffffefe4) + val colorOrange400 = Color(0xffffdfc8) + val colorOrange500 = Color(0xffffc8a1) + val colorOrange600 = Color(0xfffdb37c) + val colorOrange700 = Color(0xfff89440) + val colorOrange800 = Color(0xffdc6700) + val colorOrange900 = Color(0xffbc4500) + val colorPink100 = Color(0xfffffafb) + val colorPink1000 = Color(0xffb80a5b) + val colorPink1100 = Color(0xff9f0850) + val colorPink1200 = Color(0xff7e0642) + val colorPink1300 = Color(0xff5f002b) + val colorPink1400 = Color(0xff430017) + val colorPink200 = Color(0xfffff5f7) + val colorPink300 = Color(0xffffecf0) + val colorPink400 = Color(0xffffdee5) + val colorPink500 = Color(0xffffc2cf) + val colorPink600 = Color(0xffffadc0) + val colorPink700 = Color(0xffff88a6) + val colorPink800 = Color(0xfff7407d) + val colorPink900 = Color(0xffd20c65) + val colorPurple100 = Color(0xfffbfbff) + val colorPurple1000 = Color(0xff6b37de) + val colorPurple1100 = Color(0xff5d26cd) + val colorPurple1200 = Color(0xff4c05b5) + val colorPurple1300 = Color(0xff33008d) + val colorPurple1400 = Color(0xff200066) + val colorPurple200 = Color(0xfff8f7ff) + val colorPurple300 = Color(0xfff1efff) + val colorPurple400 = Color(0xffe6e2ff) + val colorPurple500 = Color(0xffd4cdff) + val colorPurple600 = Color(0xffc5bbff) + val colorPurple700 = Color(0xffb1a0ff) + val colorPurple800 = Color(0xff9271fd) + val colorPurple900 = Color(0xff7a47f1) + val colorRed100 = Color(0xfffffaf9) + val colorRed1000 = Color(0xffbc0f22) + val colorRed1100 = Color(0xffa4041d) + val colorRed1200 = Color(0xff850006) + val colorRed1300 = Color(0xff620000) + val colorRed1400 = Color(0xff450000) + val colorRed200 = Color(0xfffff7f6) + val colorRed300 = Color(0xffffefec) + val colorRed400 = Color(0xffffdfda) + val colorRed500 = Color(0xffffc5bc) + val colorRed600 = Color(0xffffafa5) + val colorRed700 = Color(0xffff8c81) + val colorRed800 = Color(0xffff3d3d) + val colorRed900 = Color(0xffd51928) + val colorThemeBg = Color(0xffffffff) + val colorTransparent = Color(0x00000000) + val colorYellow100 = Color(0xfffffcf0) + val colorYellow1000 = Color(0xff8f4d00) + val colorYellow1100 = Color(0xff803f00) + val colorYellow1200 = Color(0xff692e00) + val colorYellow1300 = Color(0xff541a00) + val colorYellow1400 = Color(0xff410600) + val colorYellow200 = Color(0xfffff8e0) + val colorYellow300 = Color(0xfffff2c1) + val colorYellow400 = Color(0xffffe484) + val colorYellow500 = Color(0xfffbce00) + val colorYellow600 = Color(0xfff1bd00) + val colorYellow700 = Color(0xffdea200) + val colorYellow800 = Color(0xffbe7a00) + val colorYellow900 = Color(0xff9f5b00) +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/internal/LightHcColorTokens.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/internal/LightHcColorTokens.kt new file mode 100644 index 0000000000..86624bb597 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/internal/LightHcColorTokens.kt @@ -0,0 +1,335 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +/** + * !!! WARNING !!! + * + * THIS IS AN AUTOGENERATED FILE. + * DO NOT EDIT MANUALLY. + */ + + + +@file:Suppress("all") +package io.element.android.compound.tokens.generated.internal + +import androidx.compose.ui.graphics.Color +import io.element.android.compound.annotations.CoreColorToken + +@CoreColorToken +object LightHcColorTokens { + val colorAlphaBlue100 = Color(0x0d2474ff) + val colorAlphaBlue1000 = Color(0xfc023997) + val colorAlphaBlue1100 = Color(0xfc012e89) + val colorAlphaBlue1200 = Color(0xfc00257a) + val colorAlphaBlue1300 = Color(0xff00156b) + val colorAlphaBlue1400 = Color(0xff000b61) + val colorAlphaBlue200 = Color(0x170a70ff) + val colorAlphaBlue300 = Color(0x290b6af9) + val colorAlphaBlue400 = Color(0x380565f5) + val colorAlphaBlue500 = Color(0x5e0663ef) + val colorAlphaBlue600 = Color(0x820264ed) + val colorAlphaBlue700 = Color(0xb50062eb) + val colorAlphaBlue800 = Color(0xfc016ee9) + val colorAlphaBlue900 = Color(0xfc0241a7) + val colorAlphaCyan100 = Color(0x0f16abbb) + val colorAlphaCyan1000 = Color(0xff00437a) + val colorAlphaCyan1100 = Color(0xff003870) + val colorAlphaCyan1200 = Color(0xff003066) + val colorAlphaCyan1300 = Color(0xff001e52) + val colorAlphaCyan1400 = Color(0xff00174d) + val colorAlphaCyan200 = Color(0x1c00a8c2) + val colorAlphaCyan300 = Color(0x3800aabd) + val colorAlphaCyan400 = Color(0x4f03a9bf) + val colorAlphaCyan500 = Color(0x8a01aac1) + val colorAlphaCyan600 = Color(0xeb01b7cb) + val colorAlphaCyan700 = Color(0xff0098c2) + val colorAlphaCyan800 = Color(0xff007ab3) + val colorAlphaCyan900 = Color(0xff004d85) + val colorAlphaFuchsia100 = Color(0x0ab505cc) + val colorAlphaFuchsia1000 = Color(0xe85e007a) + val colorAlphaFuchsia1100 = Color(0xf253026f) + val colorAlphaFuchsia1200 = Color(0xff53026e) + val colorAlphaFuchsia1300 = Color(0xff3a0052) + val colorAlphaFuchsia1400 = Color(0xff34004d) + val colorAlphaFuchsia200 = Color(0x12b60cc6) + val colorAlphaFuchsia300 = Color(0x21bd09c3) + val colorAlphaFuchsia400 = Color(0x2eb105bd) + val colorAlphaFuchsia500 = Color(0x4fb207bb) + val colorAlphaFuchsia600 = Color(0x6eaa04b9) + val colorAlphaFuchsia700 = Color(0x99ab03ba) + val colorAlphaFuchsia800 = Color(0xc9a402b6) + val colorAlphaFuchsia900 = Color(0xe66a0387) + val colorAlphaGray100 = Color(0x0a366881) + val colorAlphaGray1000 = Color(0xc202060d) + val colorAlphaGray1100 = Color(0xcc03060c) + val colorAlphaGray1200 = Color(0xd4020509) + val colorAlphaGray1300 = Color(0xe000040a) + val colorAlphaGray1400 = Color(0xe6010309) + val colorAlphaGray200 = Color(0x0f052657) + val colorAlphaGray300 = Color(0x1f052e61) + val colorAlphaGray400 = Color(0x29052551) + val colorAlphaGray500 = Color(0x42011d3c) + val colorAlphaGray600 = Color(0x59011532) + val colorAlphaGray700 = Color(0x7a05152e) + val colorAlphaGray800 = Color(0x94020e22) + val colorAlphaGray900 = Color(0xba030711) + val colorAlphaGreen100 = Color(0x0f16bb69) + val colorAlphaGreen1000 = Color(0xff004d36) + val colorAlphaGreen1100 = Color(0xff00422c) + val colorAlphaGreen1200 = Color(0xff003824) + val colorAlphaGreen1300 = Color(0xff002916) + val colorAlphaGreen1400 = Color(0xff002410) + val colorAlphaGreen200 = Color(0x1c00b85c) + val colorAlphaGreen300 = Color(0x3b07b661) + val colorAlphaGreen400 = Color(0x5205b867) + val colorAlphaGreen500 = Color(0x8f01b76e) + val colorAlphaGreen600 = Color(0xf501c18a) + val colorAlphaGreen700 = Color(0xff00a37d) + val colorAlphaGreen800 = Color(0xff00856a) + val colorAlphaGreen900 = Color(0xff00573e) + val colorAlphaLime100 = Color(0x1238d40c) + val colorAlphaLime1000 = Color(0xff005200) + val colorAlphaLime1100 = Color(0xff004200) + val colorAlphaLime1200 = Color(0xff003800) + val colorAlphaLime1300 = Color(0xff002900) + val colorAlphaLime1400 = Color(0xff002400) + val colorAlphaLime200 = Color(0x262ecf02) + val colorAlphaLime300 = Color(0x473ace09) + val colorAlphaLime400 = Color(0x6637cc05) + val colorAlphaLime500 = Color(0xb540ce03) + val colorAlphaLime600 = Color(0xdb39bd00) + val colorAlphaLime700 = Color(0xe6249801) + val colorAlphaLime800 = Color(0xf2127e02) + val colorAlphaLime900 = Color(0xff005700) + val colorAlphaOrange100 = Color(0x12ff7d1a) + val colorAlphaOrange1000 = Color(0xff8a0900) + val colorAlphaOrange1100 = Color(0xff750000) + val colorAlphaOrange1200 = Color(0xff660000) + val colorAlphaOrange1300 = Color(0xff4d0000) + val colorAlphaOrange1400 = Color(0xff420000) + val colorAlphaOrange200 = Color(0x1cff6c0a) + val colorAlphaOrange300 = Color(0x38ff6d05) + val colorAlphaOrange400 = Color(0x4dff700a) + val colorAlphaOrange500 = Color(0x85fc6f03) + val colorAlphaOrange600 = Color(0xbff56e00) + val colorAlphaOrange700 = Color(0xffe06c00) + val colorAlphaOrange800 = Color(0xffc24e00) + val colorAlphaOrange900 = Color(0xff941600) + val colorAlphaPink100 = Color(0x0aff0537) + val colorAlphaPink1000 = Color(0xfa830242) + val colorAlphaPink1100 = Color(0xff70003a) + val colorAlphaPink1200 = Color(0xff660030) + val colorAlphaPink1300 = Color(0xff4d001d) + val colorAlphaPink1400 = Color(0xff420015) + val colorAlphaPink200 = Color(0x14ff1447) + val colorAlphaPink300 = Color(0x21ff0037) + val colorAlphaPink400 = Color(0x30ff0a3f) + val colorAlphaPink500 = Color(0x54ff053f) + val colorAlphaPink600 = Color(0x78ff0040) + val colorAlphaPink700 = Color(0xb3f70250) + val colorAlphaPink800 = Color(0xf5de0265) + val colorAlphaPink900 = Color(0xf78f0045) + val colorAlphaPurple100 = Color(0x0a5338ff) + val colorAlphaPurple1000 = Color(0xf24600b8) + val colorAlphaPurple1100 = Color(0xff4300a8) + val colorAlphaPurple1200 = Color(0xff360094) + val colorAlphaPurple1300 = Color(0xff240070) + val colorAlphaPurple1400 = Color(0xff1f0061) + val colorAlphaPurple200 = Color(0x12381aff) + val colorAlphaPurple300 = Color(0x1f2f0fff) + val colorAlphaPurple400 = Color(0x292b0aff) + val colorAlphaPurple500 = Color(0x452b05ff) + val colorAlphaPurple600 = Color(0x613305ff) + val colorAlphaPurple700 = Color(0x873c00ff) + val colorAlphaPurple800 = Color(0xb34c02f7) + val colorAlphaPurple900 = Color(0xe64503bf) + val colorAlphaRed100 = Color(0x0aff391f) + val colorAlphaRed1000 = Color(0xff8a000b) + val colorAlphaRed1100 = Color(0xff750000) + val colorAlphaRed1200 = Color(0xff660000) + val colorAlphaRed1300 = Color(0xff4d0000) + val colorAlphaRed1400 = Color(0xff420000) + val colorAlphaRed200 = Color(0x14ff3814) + val colorAlphaRed300 = Color(0x26ff2b0a) + val colorAlphaRed400 = Color(0x36ff2605) + val colorAlphaRed500 = Color(0x5cff2205) + val colorAlphaRed600 = Color(0x80ff1a05) + val colorAlphaRed700 = Color(0xb8ff0900) + val colorAlphaRed800 = Color(0xe3de0211) + val colorAlphaRed900 = Color(0xff99001a) + val colorAlphaYellow100 = Color(0x21ffc70f) + val colorAlphaYellow1000 = Color(0xff703200) + val colorAlphaYellow1100 = Color(0xff612700) + val colorAlphaYellow1200 = Color(0xff571d00) + val colorAlphaYellow1300 = Color(0xff470c00) + val colorAlphaYellow1400 = Color(0xff3d0500) + val colorAlphaYellow200 = Color(0x40ffc905) + val colorAlphaYellow300 = Color(0x7dffc905) + val colorAlphaYellow400 = Color(0xb8ffcc00) + val colorAlphaYellow500 = Color(0xfff0bc00) + val colorAlphaYellow600 = Color(0xffe0a500) + val colorAlphaYellow700 = Color(0xffc28100) + val colorAlphaYellow800 = Color(0xffa86500) + val colorAlphaYellow900 = Color(0xff753700) + val colorBlue100 = Color(0xfff4f8ff) + val colorBlue1000 = Color(0xff053b9a) + val colorBlue1100 = Color(0xff043088) + val colorBlue1200 = Color(0xff03277b) + val colorBlue1300 = Color(0xff001569) + val colorBlue1400 = Color(0xff000c63) + val colorBlue200 = Color(0xffe9f2ff) + val colorBlue300 = Color(0xffd8e7fe) + val colorBlue400 = Color(0xffc8ddfd) + val colorBlue500 = Color(0xffa3c6fa) + val colorBlue600 = Color(0xff7eaff6) + val colorBlue700 = Color(0xff4a8ef0) + val colorBlue800 = Color(0xff046ee8) + val colorBlue900 = Color(0xff0543a7) + val colorCyan100 = Color(0xfff1fafb) + val colorCyan1000 = Color(0xff00447b) + val colorCyan1100 = Color(0xff00376e) + val colorCyan1200 = Color(0xff002e64) + val colorCyan1300 = Color(0xff001e53) + val colorCyan1400 = Color(0xff00174d) + val colorCyan200 = Color(0xffe3f5f8) + val colorCyan300 = Color(0xffc7ecf0) + val colorCyan400 = Color(0xffb1e4eb) + val colorCyan500 = Color(0xff76d1dd) + val colorCyan600 = Color(0xff15becf) + val colorCyan700 = Color(0xff009ac3) + val colorCyan800 = Color(0xff007ab3) + val colorCyan900 = Color(0xff004c84) + val colorFuchsia100 = Color(0xfffcf5fd) + val colorFuchsia1000 = Color(0xff6c1785) + val colorFuchsia1100 = Color(0xff5c0f76) + val colorFuchsia1200 = Color(0xff52026c) + val colorFuchsia1300 = Color(0xff3b0053) + val colorFuchsia1400 = Color(0xff32004a) + val colorFuchsia200 = Color(0xfffaeefb) + val colorFuchsia300 = Color(0xfff6dff7) + val colorFuchsia400 = Color(0xfff1d2f3) + val colorFuchsia500 = Color(0xffe7b2ea) + val colorFuchsia600 = Color(0xffdb93e1) + val colorFuchsia700 = Color(0xffcb68d4) + val colorFuchsia800 = Color(0xffb937c6) + val colorFuchsia900 = Color(0xff781c90) + val colorGray100 = Color(0xfff7f9fa) + val colorGray1000 = Color(0xff3f4248) + val colorGray1100 = Color(0xff35383d) + val colorGray1200 = Color(0xff2d3034) + val colorGray1300 = Color(0xff1f2126) + val colorGray1400 = Color(0xff1a1c21) + val colorGray200 = Color(0xfff0f2f5) + val colorGray300 = Color(0xffe1e6ec) + val colorGray400 = Color(0xffd7dce3) + val colorGray500 = Color(0xffbdc4cc) + val colorGray600 = Color(0xffa6adb7) + val colorGray700 = Color(0xff878f9b) + val colorGray800 = Color(0xff6c737e) + val colorGray900 = Color(0xff474a51) + val colorGreen100 = Color(0xfff1fbf6) + val colorGreen1000 = Color(0xff004d36) + val colorGreen1100 = Color(0xff00402b) + val colorGreen1200 = Color(0xff003723) + val colorGreen1300 = Color(0xff002715) + val colorGreen1400 = Color(0xff00210f) + val colorGreen200 = Color(0xffe3f7ed) + val colorGreen300 = Color(0xffc6eedb) + val colorGreen400 = Color(0xffafe8ce) + val colorGreen500 = Color(0xff71d7ae) + val colorGreen600 = Color(0xff0bc491) + val colorGreen700 = Color(0xff00a27c) + val colorGreen800 = Color(0xff008268) + val colorGreen900 = Color(0xff00553d) + val colorLime100 = Color(0xfff1fcee) + val colorLime1000 = Color(0xff004f00) + val colorLime1100 = Color(0xff004200) + val colorLime1200 = Color(0xff003900) + val colorLime1300 = Color(0xff002900) + val colorLime1400 = Color(0xff002200) + val colorLime200 = Color(0xffe0f8d9) + val colorLime300 = Color(0xffc8f1ba) + val colorLime400 = Color(0xffafeb9b) + val colorLime500 = Color(0xff76db4c) + val colorLime600 = Color(0xff54c424) + val colorLime700 = Color(0xff3aa31a) + val colorLime800 = Color(0xff1f850f) + val colorLime900 = Color(0xff005700) + val colorOrange100 = Color(0xfffff6ef) + val colorOrange1000 = Color(0xff890800) + val colorOrange1100 = Color(0xff770000) + val colorOrange1200 = Color(0xff670000) + val colorOrange1300 = Color(0xff4c0000) + val colorOrange1400 = Color(0xff420000) + val colorOrange200 = Color(0xffffefe4) + val colorOrange300 = Color(0xffffdfc8) + val colorOrange400 = Color(0xffffd4b5) + val colorOrange500 = Color(0xfffdb37c) + val colorOrange600 = Color(0xfff89440) + val colorOrange700 = Color(0xffe26e00) + val colorOrange800 = Color(0xffc44d00) + val colorOrange900 = Color(0xff931700) + val colorPink100 = Color(0xfffff5f7) + val colorPink1000 = Color(0xff840745) + val colorPink1100 = Color(0xff72003a) + val colorPink1200 = Color(0xff64002f) + val colorPink1300 = Color(0xff4a001c) + val colorPink1400 = Color(0xff410015) + val colorPink200 = Color(0xffffecf0) + val colorPink300 = Color(0xffffdee5) + val colorPink400 = Color(0xffffd0da) + val colorPink500 = Color(0xffffadc0) + val colorPink600 = Color(0xffff88a6) + val colorPink700 = Color(0xfff94e84) + val colorPink800 = Color(0xffe00c6a) + val colorPink900 = Color(0xff92084b) + val colorPurple100 = Color(0xfff8f7ff) + val colorPurple1000 = Color(0xff4f0dba) + val colorPurple1100 = Color(0xff4200a6) + val colorPurple1200 = Color(0xff360094) + val colorPurple1300 = Color(0xff240070) + val colorPurple1400 = Color(0xff1f0062) + val colorPurple200 = Color(0xfff1efff) + val colorPurple300 = Color(0xffe6e2ff) + val colorPurple400 = Color(0xffddd8ff) + val colorPurple500 = Color(0xffc5bbff) + val colorPurple600 = Color(0xffb1a0ff) + val colorPurple700 = Color(0xff9778fe) + val colorPurple800 = Color(0xff824ef9) + val colorPurple900 = Color(0xff571cc4) + val colorRed100 = Color(0xfffff7f6) + val colorRed1000 = Color(0xff8b000c) + val colorRed1100 = Color(0xff770000) + val colorRed1200 = Color(0xff670000) + val colorRed1300 = Color(0xff4c0000) + val colorRed1400 = Color(0xff420000) + val colorRed200 = Color(0xffffefec) + val colorRed300 = Color(0xffffdfda) + val colorRed400 = Color(0xffffd1ca) + val colorRed500 = Color(0xffffafa5) + val colorRed600 = Color(0xffff8c81) + val colorRed700 = Color(0xffff4e49) + val colorRed800 = Color(0xffe11e2a) + val colorRed900 = Color(0xff99001a) + val colorThemeBg = Color(0xffffffff) + val colorTransparent = Color(0x00000000) + val colorYellow100 = Color(0xfffff8e0) + val colorYellow1000 = Color(0xff6e3100) + val colorYellow1100 = Color(0xff612600) + val colorYellow1200 = Color(0xff571d00) + val colorYellow1300 = Color(0xff450c00) + val colorYellow1400 = Color(0xff3f0500) + val colorYellow200 = Color(0xfffff2c1) + val colorYellow300 = Color(0xffffe484) + val colorYellow400 = Color(0xffffda49) + val colorYellow500 = Color(0xfff1bd00) + val colorYellow600 = Color(0xffdea200) + val colorYellow700 = Color(0xffc38100) + val colorYellow800 = Color(0xffa76300) + val colorYellow900 = Color(0xff773800) +} diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/utils/ColorUtils.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/utils/ColorUtils.kt new file mode 100644 index 0000000000..f0c6c12fb0 --- /dev/null +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/utils/ColorUtils.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2023, 2024 New Vector 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.compound.utils + +import androidx.compose.ui.graphics.Color + +/** + * Convert color to Human Readable Format. + */ +fun Color.toHrf(): String { + return "0x" + value.toString(16).take(8).uppercase() +} diff --git a/libraries/compound/src/main/res/drawable/ic_compound_admin.xml b/libraries/compound/src/main/res/drawable/ic_compound_admin.xml new file mode 100644 index 0000000000..8762195e7f --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_admin.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_arrow_down.xml b/libraries/compound/src/main/res/drawable/ic_compound_arrow_down.xml new file mode 100644 index 0000000000..6d311ac7a2 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_arrow_down.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_arrow_left.xml b/libraries/compound/src/main/res/drawable/ic_compound_arrow_left.xml new file mode 100644 index 0000000000..4e1949895f --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_arrow_left.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_arrow_right.xml b/libraries/compound/src/main/res/drawable/ic_compound_arrow_right.xml new file mode 100644 index 0000000000..4a42e3c16f --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_arrow_right.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_arrow_up.xml b/libraries/compound/src/main/res/drawable/ic_compound_arrow_up.xml new file mode 100644 index 0000000000..7ff26b539a --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_arrow_up.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_arrow_up_right.xml b/libraries/compound/src/main/res/drawable/ic_compound_arrow_up_right.xml new file mode 100644 index 0000000000..ea686b6cff --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_arrow_up_right.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_ask_to_join.xml b/libraries/compound/src/main/res/drawable/ic_compound_ask_to_join.xml new file mode 100644 index 0000000000..2c2f779ab6 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_ask_to_join.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_ask_to_join_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_ask_to_join_solid.xml new file mode 100644 index 0000000000..1346e2c889 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_ask_to_join_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_attachment.xml b/libraries/compound/src/main/res/drawable/ic_compound_attachment.xml new file mode 100644 index 0000000000..4ddf7f4df7 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_attachment.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_audio.xml b/libraries/compound/src/main/res/drawable/ic_compound_audio.xml new file mode 100644 index 0000000000..5fb9365470 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_audio.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_block.xml b/libraries/compound/src/main/res/drawable/ic_compound_block.xml new file mode 100644 index 0000000000..08ef51dce3 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_block.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_bold.xml b/libraries/compound/src/main/res/drawable/ic_compound_bold.xml new file mode 100644 index 0000000000..2546a230cc --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_bold.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_calendar.xml b/libraries/compound/src/main/res/drawable/ic_compound_calendar.xml new file mode 100644 index 0000000000..72a9fe5868 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_calendar.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_chart.xml b/libraries/compound/src/main/res/drawable/ic_compound_chart.xml new file mode 100644 index 0000000000..cc1c8fb662 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_chart.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_chat.xml b/libraries/compound/src/main/res/drawable/ic_compound_chat.xml new file mode 100644 index 0000000000..3a7e70841b --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_chat.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_chat_new.xml b/libraries/compound/src/main/res/drawable/ic_compound_chat_new.xml new file mode 100644 index 0000000000..8bf9f6762f --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_chat_new.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_chat_problem.xml b/libraries/compound/src/main/res/drawable/ic_compound_chat_problem.xml new file mode 100644 index 0000000000..380358478a --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_chat_problem.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_chat_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_chat_solid.xml new file mode 100644 index 0000000000..d08def35fe --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_chat_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_check.xml b/libraries/compound/src/main/res/drawable/ic_compound_check.xml new file mode 100644 index 0000000000..78f935c940 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_check.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_check_circle.xml b/libraries/compound/src/main/res/drawable/ic_compound_check_circle.xml new file mode 100644 index 0000000000..8d6147246b --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_check_circle.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_check_circle_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_check_circle_solid.xml new file mode 100644 index 0000000000..d258a5c97a --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_check_circle_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_chevron_down.xml b/libraries/compound/src/main/res/drawable/ic_compound_chevron_down.xml new file mode 100644 index 0000000000..2ac456e988 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_chevron_down.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_chevron_left.xml b/libraries/compound/src/main/res/drawable/ic_compound_chevron_left.xml new file mode 100644 index 0000000000..50316a4e81 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_chevron_left.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_chevron_right.xml b/libraries/compound/src/main/res/drawable/ic_compound_chevron_right.xml new file mode 100644 index 0000000000..d19a9daa4d --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_chevron_right.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_chevron_up.xml b/libraries/compound/src/main/res/drawable/ic_compound_chevron_up.xml new file mode 100644 index 0000000000..d84ebade9c --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_chevron_up.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_chevron_up_down.xml b/libraries/compound/src/main/res/drawable/ic_compound_chevron_up_down.xml new file mode 100644 index 0000000000..213dfbef9f --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_chevron_up_down.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_circle.xml b/libraries/compound/src/main/res/drawable/ic_compound_circle.xml new file mode 100644 index 0000000000..d5a292aa3c --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_circle.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_close.xml b/libraries/compound/src/main/res/drawable/ic_compound_close.xml new file mode 100644 index 0000000000..ef8a75f08a --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_close.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_cloud.xml b/libraries/compound/src/main/res/drawable/ic_compound_cloud.xml new file mode 100644 index 0000000000..b96fd18250 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_cloud.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_cloud_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_cloud_solid.xml new file mode 100644 index 0000000000..09a91809f3 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_cloud_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_code.xml b/libraries/compound/src/main/res/drawable/ic_compound_code.xml new file mode 100644 index 0000000000..c17c73c201 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_code.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_collapse.xml b/libraries/compound/src/main/res/drawable/ic_compound_collapse.xml new file mode 100644 index 0000000000..8cc9302197 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_collapse.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_company.xml b/libraries/compound/src/main/res/drawable/ic_compound_company.xml new file mode 100644 index 0000000000..28a9232c13 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_company.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_compose.xml b/libraries/compound/src/main/res/drawable/ic_compound_compose.xml new file mode 100644 index 0000000000..eaa9ca0ef1 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_compose.xml @@ -0,0 +1,14 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_computer.xml b/libraries/compound/src/main/res/drawable/ic_compound_computer.xml new file mode 100644 index 0000000000..97748e8ffd --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_computer.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_copy.xml b/libraries/compound/src/main/res/drawable/ic_compound_copy.xml new file mode 100644 index 0000000000..4492c5aca9 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_copy.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_dark_mode.xml b/libraries/compound/src/main/res/drawable/ic_compound_dark_mode.xml new file mode 100644 index 0000000000..bc4fa89cd4 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_dark_mode.xml @@ -0,0 +1,11 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_delete.xml b/libraries/compound/src/main/res/drawable/ic_compound_delete.xml new file mode 100644 index 0000000000..dd2e3b50d0 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_delete.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_devices.xml b/libraries/compound/src/main/res/drawable/ic_compound_devices.xml new file mode 100644 index 0000000000..a399d675cf --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_devices.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_dial_pad.xml b/libraries/compound/src/main/res/drawable/ic_compound_dial_pad.xml new file mode 100644 index 0000000000..1967b8df77 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_dial_pad.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_document.xml b/libraries/compound/src/main/res/drawable/ic_compound_document.xml new file mode 100644 index 0000000000..830a36597a --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_document.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_download.xml b/libraries/compound/src/main/res/drawable/ic_compound_download.xml new file mode 100644 index 0000000000..bf4c24ec26 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_download.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_download_ios.xml b/libraries/compound/src/main/res/drawable/ic_compound_download_ios.xml new file mode 100644 index 0000000000..16a45ee188 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_download_ios.xml @@ -0,0 +1,12 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_drag_grid.xml b/libraries/compound/src/main/res/drawable/ic_compound_drag_grid.xml new file mode 100644 index 0000000000..96697b574d --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_drag_grid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_drag_list.xml b/libraries/compound/src/main/res/drawable/ic_compound_drag_list.xml new file mode 100644 index 0000000000..57fe86c0e8 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_drag_list.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_earpiece.xml b/libraries/compound/src/main/res/drawable/ic_compound_earpiece.xml new file mode 100644 index 0000000000..66b495b304 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_earpiece.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_edit.xml b/libraries/compound/src/main/res/drawable/ic_compound_edit.xml new file mode 100644 index 0000000000..de0c0b345a --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_edit.xml @@ -0,0 +1,11 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_edit_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_edit_solid.xml new file mode 100644 index 0000000000..33da283f4d --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_edit_solid.xml @@ -0,0 +1,11 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_email.xml b/libraries/compound/src/main/res/drawable/ic_compound_email.xml new file mode 100644 index 0000000000..223c9620f3 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_email.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_email_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_email_solid.xml new file mode 100644 index 0000000000..f5c98f8cfa --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_email_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_end_call.xml b/libraries/compound/src/main/res/drawable/ic_compound_end_call.xml new file mode 100644 index 0000000000..f58a645c6e --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_end_call.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_error.xml b/libraries/compound/src/main/res/drawable/ic_compound_error.xml new file mode 100644 index 0000000000..a4ea2f0016 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_error.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_error_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_error_solid.xml new file mode 100644 index 0000000000..97b8ece183 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_error_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_expand.xml b/libraries/compound/src/main/res/drawable/ic_compound_expand.xml new file mode 100644 index 0000000000..88aef8abc1 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_expand.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_explore.xml b/libraries/compound/src/main/res/drawable/ic_compound_explore.xml new file mode 100644 index 0000000000..9b62efa291 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_explore.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_export_archive.xml b/libraries/compound/src/main/res/drawable/ic_compound_export_archive.xml new file mode 100644 index 0000000000..f09775d0be --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_export_archive.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_extensions.xml b/libraries/compound/src/main/res/drawable/ic_compound_extensions.xml new file mode 100644 index 0000000000..1bd8ae7bce --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_extensions.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_extensions_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_extensions_solid.xml new file mode 100644 index 0000000000..7873a49ed6 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_extensions_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_favourite.xml b/libraries/compound/src/main/res/drawable/ic_compound_favourite.xml new file mode 100644 index 0000000000..f7845dc080 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_favourite.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_favourite_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_favourite_solid.xml new file mode 100644 index 0000000000..3fc393c572 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_favourite_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_file_error.xml b/libraries/compound/src/main/res/drawable/ic_compound_file_error.xml new file mode 100644 index 0000000000..13b3571ef8 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_file_error.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_files.xml b/libraries/compound/src/main/res/drawable/ic_compound_files.xml new file mode 100644 index 0000000000..67a30b6bd8 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_files.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_filter.xml b/libraries/compound/src/main/res/drawable/ic_compound_filter.xml new file mode 100644 index 0000000000..2f2b0e5f10 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_filter.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_forward.xml b/libraries/compound/src/main/res/drawable/ic_compound_forward.xml new file mode 100644 index 0000000000..e614fabb4e --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_forward.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_grid.xml b/libraries/compound/src/main/res/drawable/ic_compound_grid.xml new file mode 100644 index 0000000000..223ed40a2f --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_grid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_group.xml b/libraries/compound/src/main/res/drawable/ic_compound_group.xml new file mode 100644 index 0000000000..4781d1306b --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_group.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_guest.xml b/libraries/compound/src/main/res/drawable/ic_compound_guest.xml new file mode 100644 index 0000000000..1bb2d99842 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_guest.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_headphones_off_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_headphones_off_solid.xml new file mode 100644 index 0000000000..af81d29c1d --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_headphones_off_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_headphones_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_headphones_solid.xml new file mode 100644 index 0000000000..eac3a666d0 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_headphones_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_help.xml b/libraries/compound/src/main/res/drawable/ic_compound_help.xml new file mode 100644 index 0000000000..293120e8cd --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_help.xml @@ -0,0 +1,12 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_help_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_help_solid.xml new file mode 100644 index 0000000000..80332718c2 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_help_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_history.xml b/libraries/compound/src/main/res/drawable/ic_compound_history.xml new file mode 100644 index 0000000000..25d4c68509 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_history.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_home.xml b/libraries/compound/src/main/res/drawable/ic_compound_home.xml new file mode 100644 index 0000000000..0feb167199 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_home.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_home_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_home_solid.xml new file mode 100644 index 0000000000..10cca67a37 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_home_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_host.xml b/libraries/compound/src/main/res/drawable/ic_compound_host.xml new file mode 100644 index 0000000000..d45d1c0278 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_host.xml @@ -0,0 +1,14 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_image.xml b/libraries/compound/src/main/res/drawable/ic_compound_image.xml new file mode 100644 index 0000000000..58a3525b3a --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_image.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_image_error.xml b/libraries/compound/src/main/res/drawable/ic_compound_image_error.xml new file mode 100644 index 0000000000..1c8d716166 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_image_error.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_indent_decrease.xml b/libraries/compound/src/main/res/drawable/ic_compound_indent_decrease.xml new file mode 100644 index 0000000000..5ada985743 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_indent_decrease.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_indent_increase.xml b/libraries/compound/src/main/res/drawable/ic_compound_indent_increase.xml new file mode 100644 index 0000000000..de5df3977a --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_indent_increase.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_info.xml b/libraries/compound/src/main/res/drawable/ic_compound_info.xml new file mode 100644 index 0000000000..cf0318bb61 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_info.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_info_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_info_solid.xml new file mode 100644 index 0000000000..9101aacffc --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_info_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_inline_code.xml b/libraries/compound/src/main/res/drawable/ic_compound_inline_code.xml new file mode 100644 index 0000000000..9f78e1f8c2 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_inline_code.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_italic.xml b/libraries/compound/src/main/res/drawable/ic_compound_italic.xml new file mode 100644 index 0000000000..e3fee1f82e --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_italic.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_key.xml b/libraries/compound/src/main/res/drawable/ic_compound_key.xml new file mode 100644 index 0000000000..112c16d78f --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_key.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_key_off.xml b/libraries/compound/src/main/res/drawable/ic_compound_key_off.xml new file mode 100644 index 0000000000..b160e81c73 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_key_off.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_key_off_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_key_off_solid.xml new file mode 100644 index 0000000000..c1961ec390 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_key_off_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_key_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_key_solid.xml new file mode 100644 index 0000000000..29f9ad1a42 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_key_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_keyboard.xml b/libraries/compound/src/main/res/drawable/ic_compound_keyboard.xml new file mode 100644 index 0000000000..6216c1ccf8 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_keyboard.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_labs.xml b/libraries/compound/src/main/res/drawable/ic_compound_labs.xml new file mode 100644 index 0000000000..d0bcae3bab --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_labs.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_leave.xml b/libraries/compound/src/main/res/drawable/ic_compound_leave.xml new file mode 100644 index 0000000000..ad5897d4f2 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_leave.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_link.xml b/libraries/compound/src/main/res/drawable/ic_compound_link.xml new file mode 100644 index 0000000000..3787cf3981 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_link.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_linux.xml b/libraries/compound/src/main/res/drawable/ic_compound_linux.xml new file mode 100644 index 0000000000..bb3216e729 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_linux.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_list_bulleted.xml b/libraries/compound/src/main/res/drawable/ic_compound_list_bulleted.xml new file mode 100644 index 0000000000..f7fe7a8256 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_list_bulleted.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_list_numbered.xml b/libraries/compound/src/main/res/drawable/ic_compound_list_numbered.xml new file mode 100644 index 0000000000..d3fbeb2d2d --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_list_numbered.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_list_view.xml b/libraries/compound/src/main/res/drawable/ic_compound_list_view.xml new file mode 100644 index 0000000000..ffd3359cfa --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_list_view.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_location_navigator.xml b/libraries/compound/src/main/res/drawable/ic_compound_location_navigator.xml new file mode 100644 index 0000000000..bffa2d9f9d --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_location_navigator.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_location_navigator_centred.xml b/libraries/compound/src/main/res/drawable/ic_compound_location_navigator_centred.xml new file mode 100644 index 0000000000..50f8ac655d --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_location_navigator_centred.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_location_pin.xml b/libraries/compound/src/main/res/drawable/ic_compound_location_pin.xml new file mode 100644 index 0000000000..063076d4ae --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_location_pin.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_location_pin_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_location_pin_solid.xml new file mode 100644 index 0000000000..defb2d4e42 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_location_pin_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_lock.xml b/libraries/compound/src/main/res/drawable/ic_compound_lock.xml new file mode 100644 index 0000000000..9715295d94 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_lock.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_lock_off.xml b/libraries/compound/src/main/res/drawable/ic_compound_lock_off.xml new file mode 100644 index 0000000000..a19bac4811 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_lock_off.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_lock_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_lock_solid.xml new file mode 100644 index 0000000000..6a5a11b4de --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_lock_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_mac.xml b/libraries/compound/src/main/res/drawable/ic_compound_mac.xml new file mode 100644 index 0000000000..6f72038f43 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_mac.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_mark_as_read.xml b/libraries/compound/src/main/res/drawable/ic_compound_mark_as_read.xml new file mode 100644 index 0000000000..8de67ac4e8 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_mark_as_read.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_mark_as_unread.xml b/libraries/compound/src/main/res/drawable/ic_compound_mark_as_unread.xml new file mode 100644 index 0000000000..6b3c53144a --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_mark_as_unread.xml @@ -0,0 +1,14 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_mark_threads_as_read.xml b/libraries/compound/src/main/res/drawable/ic_compound_mark_threads_as_read.xml new file mode 100644 index 0000000000..5d6c12a557 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_mark_threads_as_read.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_marker_read_receipts.xml b/libraries/compound/src/main/res/drawable/ic_compound_marker_read_receipts.xml new file mode 100644 index 0000000000..c70cfd3e51 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_marker_read_receipts.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_mention.xml b/libraries/compound/src/main/res/drawable/ic_compound_mention.xml new file mode 100644 index 0000000000..274ccb3775 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_mention.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_menu.xml b/libraries/compound/src/main/res/drawable/ic_compound_menu.xml new file mode 100644 index 0000000000..c3ee1a2ee1 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_menu.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_mic_off.xml b/libraries/compound/src/main/res/drawable/ic_compound_mic_off.xml new file mode 100644 index 0000000000..e28210c07d --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_mic_off.xml @@ -0,0 +1,14 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_mic_off_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_mic_off_solid.xml new file mode 100644 index 0000000000..686809342a --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_mic_off_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_mic_on.xml b/libraries/compound/src/main/res/drawable/ic_compound_mic_on.xml new file mode 100644 index 0000000000..793b9c8f83 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_mic_on.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_mic_on_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_mic_on_solid.xml new file mode 100644 index 0000000000..026f477e75 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_mic_on_solid.xml @@ -0,0 +1,12 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_minus.xml b/libraries/compound/src/main/res/drawable/ic_compound_minus.xml new file mode 100644 index 0000000000..064946b6dc --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_minus.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_mobile.xml b/libraries/compound/src/main/res/drawable/ic_compound_mobile.xml new file mode 100644 index 0000000000..8257628868 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_mobile.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_notifications.xml b/libraries/compound/src/main/res/drawable/ic_compound_notifications.xml new file mode 100644 index 0000000000..afd16aa8e9 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_notifications.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_notifications_off.xml b/libraries/compound/src/main/res/drawable/ic_compound_notifications_off.xml new file mode 100644 index 0000000000..e4fca897a3 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_notifications_off.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_notifications_off_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_notifications_off_solid.xml new file mode 100644 index 0000000000..4bc9fdd2ab --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_notifications_off_solid.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_notifications_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_notifications_solid.xml new file mode 100644 index 0000000000..358a18c83e --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_notifications_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_offline.xml b/libraries/compound/src/main/res/drawable/ic_compound_offline.xml new file mode 100644 index 0000000000..322954e4b0 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_offline.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_overflow_horizontal.xml b/libraries/compound/src/main/res/drawable/ic_compound_overflow_horizontal.xml new file mode 100644 index 0000000000..c85d1e8da2 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_overflow_horizontal.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_overflow_vertical.xml b/libraries/compound/src/main/res/drawable/ic_compound_overflow_vertical.xml new file mode 100644 index 0000000000..269e28613c --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_overflow_vertical.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_pause.xml b/libraries/compound/src/main/res/drawable/ic_compound_pause.xml new file mode 100644 index 0000000000..bcd8325107 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_pause.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_pause_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_pause_solid.xml new file mode 100644 index 0000000000..f25a7cbcfc --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_pause_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_pin.xml b/libraries/compound/src/main/res/drawable/ic_compound_pin.xml new file mode 100644 index 0000000000..0aa36f53e9 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_pin.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_pin_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_pin_solid.xml new file mode 100644 index 0000000000..9326b0fd75 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_pin_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_play.xml b/libraries/compound/src/main/res/drawable/ic_compound_play.xml new file mode 100644 index 0000000000..d7b8d3c5e5 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_play.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_play_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_play_solid.xml new file mode 100644 index 0000000000..fca650ccd8 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_play_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_plus.xml b/libraries/compound/src/main/res/drawable/ic_compound_plus.xml new file mode 100644 index 0000000000..a20a59aac9 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_plus.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_polls.xml b/libraries/compound/src/main/res/drawable/ic_compound_polls.xml new file mode 100644 index 0000000000..8c0e2b5a45 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_polls.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_polls_end.xml b/libraries/compound/src/main/res/drawable/ic_compound_polls_end.xml new file mode 100644 index 0000000000..8cfe2be80e --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_polls_end.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_pop_out.xml b/libraries/compound/src/main/res/drawable/ic_compound_pop_out.xml new file mode 100644 index 0000000000..7b5b07b969 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_pop_out.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_preferences.xml b/libraries/compound/src/main/res/drawable/ic_compound_preferences.xml new file mode 100644 index 0000000000..fbc271730d --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_preferences.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_presence_outline_8_x_8.xml b/libraries/compound/src/main/res/drawable/ic_compound_presence_outline_8_x_8.xml new file mode 100644 index 0000000000..3d815c0696 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_presence_outline_8_x_8.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_presence_solid_8_x_8.xml b/libraries/compound/src/main/res/drawable/ic_compound_presence_solid_8_x_8.xml new file mode 100644 index 0000000000..ab82df2441 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_presence_solid_8_x_8.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_presence_strikethrough_8_x_8.xml b/libraries/compound/src/main/res/drawable/ic_compound_presence_strikethrough_8_x_8.xml new file mode 100644 index 0000000000..1ed8a1e492 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_presence_strikethrough_8_x_8.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_public.xml b/libraries/compound/src/main/res/drawable/ic_compound_public.xml new file mode 100644 index 0000000000..3dfd7046ea --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_public.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_qr_code.xml b/libraries/compound/src/main/res/drawable/ic_compound_qr_code.xml new file mode 100644 index 0000000000..4befbcef95 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_qr_code.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_quote.xml b/libraries/compound/src/main/res/drawable/ic_compound_quote.xml new file mode 100644 index 0000000000..728fe07e43 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_quote.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_raised_hand_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_raised_hand_solid.xml new file mode 100644 index 0000000000..d345569ead --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_raised_hand_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_reaction.xml b/libraries/compound/src/main/res/drawable/ic_compound_reaction.xml new file mode 100644 index 0000000000..f85f57d002 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_reaction.xml @@ -0,0 +1,12 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_reaction_add.xml b/libraries/compound/src/main/res/drawable/ic_compound_reaction_add.xml new file mode 100644 index 0000000000..3fe88d4f8a --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_reaction_add.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_reaction_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_reaction_solid.xml new file mode 100644 index 0000000000..826ac69830 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_reaction_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_reply.xml b/libraries/compound/src/main/res/drawable/ic_compound_reply.xml new file mode 100644 index 0000000000..45a797deda --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_reply.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_restart.xml b/libraries/compound/src/main/res/drawable/ic_compound_restart.xml new file mode 100644 index 0000000000..9360979845 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_restart.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_room.xml b/libraries/compound/src/main/res/drawable/ic_compound_room.xml new file mode 100644 index 0000000000..a0a278eab7 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_room.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_search.xml b/libraries/compound/src/main/res/drawable/ic_compound_search.xml new file mode 100644 index 0000000000..8e1ec94374 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_search.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_send.xml b/libraries/compound/src/main/res/drawable/ic_compound_send.xml new file mode 100644 index 0000000000..8c0d5e1159 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_send.xml @@ -0,0 +1,11 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_send_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_send_solid.xml new file mode 100644 index 0000000000..3ac0bc6f62 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_send_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_settings.xml b/libraries/compound/src/main/res/drawable/ic_compound_settings.xml new file mode 100644 index 0000000000..7a79aa33ec --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_settings.xml @@ -0,0 +1,12 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_settings_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_settings_solid.xml new file mode 100644 index 0000000000..7a75b037ba --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_settings_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_share.xml b/libraries/compound/src/main/res/drawable/ic_compound_share.xml new file mode 100644 index 0000000000..6abf169f20 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_share.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_share_android.xml b/libraries/compound/src/main/res/drawable/ic_compound_share_android.xml new file mode 100644 index 0000000000..a92b79a76a --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_share_android.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_share_ios.xml b/libraries/compound/src/main/res/drawable/ic_compound_share_ios.xml new file mode 100644 index 0000000000..e481978c28 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_share_ios.xml @@ -0,0 +1,12 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_share_screen.xml b/libraries/compound/src/main/res/drawable/ic_compound_share_screen.xml new file mode 100644 index 0000000000..89f63b897c --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_share_screen.xml @@ -0,0 +1,12 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_share_screen_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_share_screen_solid.xml new file mode 100644 index 0000000000..ee9a1aa837 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_share_screen_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_shield.xml b/libraries/compound/src/main/res/drawable/ic_compound_shield.xml new file mode 100644 index 0000000000..7d2ee79157 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_shield.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_sidebar.xml b/libraries/compound/src/main/res/drawable/ic_compound_sidebar.xml new file mode 100644 index 0000000000..4bd5a0b3e3 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_sidebar.xml @@ -0,0 +1,11 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_sign_out.xml b/libraries/compound/src/main/res/drawable/ic_compound_sign_out.xml new file mode 100644 index 0000000000..a6ded2b7a8 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_sign_out.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_spinner.xml b/libraries/compound/src/main/res/drawable/ic_compound_spinner.xml new file mode 100644 index 0000000000..80721fdce8 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_spinner.xml @@ -0,0 +1,11 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_spotlight.xml b/libraries/compound/src/main/res/drawable/ic_compound_spotlight.xml new file mode 100644 index 0000000000..acaad53b3b --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_spotlight.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_spotlight_view.xml b/libraries/compound/src/main/res/drawable/ic_compound_spotlight_view.xml new file mode 100644 index 0000000000..724cdf91cf --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_spotlight_view.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_strikethrough.xml b/libraries/compound/src/main/res/drawable/ic_compound_strikethrough.xml new file mode 100644 index 0000000000..a472c6c97e --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_strikethrough.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_switch_camera_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_switch_camera_solid.xml new file mode 100644 index 0000000000..a7695e40ff --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_switch_camera_solid.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_take_photo.xml b/libraries/compound/src/main/res/drawable/ic_compound_take_photo.xml new file mode 100644 index 0000000000..d6dbb4d568 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_take_photo.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_take_photo_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_take_photo_solid.xml new file mode 100644 index 0000000000..2db8a6cdf0 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_take_photo_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_text_formatting.xml b/libraries/compound/src/main/res/drawable/ic_compound_text_formatting.xml new file mode 100644 index 0000000000..4126080fa9 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_text_formatting.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_threads.xml b/libraries/compound/src/main/res/drawable/ic_compound_threads.xml new file mode 100644 index 0000000000..4fa1e877d1 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_threads.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_threads_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_threads_solid.xml new file mode 100644 index 0000000000..4db292231d --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_threads_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_time.xml b/libraries/compound/src/main/res/drawable/ic_compound_time.xml new file mode 100644 index 0000000000..b29bb29de7 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_time.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_underline.xml b/libraries/compound/src/main/res/drawable/ic_compound_underline.xml new file mode 100644 index 0000000000..c90d6bc591 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_underline.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_unknown.xml b/libraries/compound/src/main/res/drawable/ic_compound_unknown.xml new file mode 100644 index 0000000000..87afe69e02 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_unknown.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_unknown_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_unknown_solid.xml new file mode 100644 index 0000000000..89b6a89056 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_unknown_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_unpin.xml b/libraries/compound/src/main/res/drawable/ic_compound_unpin.xml new file mode 100644 index 0000000000..c7aee251f4 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_unpin.xml @@ -0,0 +1,14 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_user.xml b/libraries/compound/src/main/res/drawable/ic_compound_user.xml new file mode 100644 index 0000000000..948bda3e4e --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_user.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_user_add.xml b/libraries/compound/src/main/res/drawable/ic_compound_user_add.xml new file mode 100644 index 0000000000..81d9aaf1bb --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_user_add.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_user_add_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_user_add_solid.xml new file mode 100644 index 0000000000..693db72c06 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_user_add_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_user_profile.xml b/libraries/compound/src/main/res/drawable/ic_compound_user_profile.xml new file mode 100644 index 0000000000..222b9510ea --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_user_profile.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_user_profile_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_user_profile_solid.xml new file mode 100644 index 0000000000..5d268642e0 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_user_profile_solid.xml @@ -0,0 +1,12 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_user_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_user_solid.xml new file mode 100644 index 0000000000..c281d6f009 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_user_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_verified.xml b/libraries/compound/src/main/res/drawable/ic_compound_verified.xml new file mode 100644 index 0000000000..f30bec93ff --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_verified.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_video_call.xml b/libraries/compound/src/main/res/drawable/ic_compound_video_call.xml new file mode 100644 index 0000000000..4f1c9b1ba7 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_video_call.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_video_call_declined_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_video_call_declined_solid.xml new file mode 100644 index 0000000000..a6b699c6bd --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_video_call_declined_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_video_call_missed_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_video_call_missed_solid.xml new file mode 100644 index 0000000000..57c1502560 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_video_call_missed_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_video_call_off.xml b/libraries/compound/src/main/res/drawable/ic_compound_video_call_off.xml new file mode 100644 index 0000000000..6466ec2848 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_video_call_off.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_video_call_off_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_video_call_off_solid.xml new file mode 100644 index 0000000000..35a5796577 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_video_call_off_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_video_call_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_video_call_solid.xml new file mode 100644 index 0000000000..ec4cf0902e --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_video_call_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_visibility_off.xml b/libraries/compound/src/main/res/drawable/ic_compound_visibility_off.xml new file mode 100644 index 0000000000..c4234584a3 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_visibility_off.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_visibility_on.xml b/libraries/compound/src/main/res/drawable/ic_compound_visibility_on.xml new file mode 100644 index 0000000000..a66dcfa429 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_visibility_on.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_voice_call.xml b/libraries/compound/src/main/res/drawable/ic_compound_voice_call.xml new file mode 100644 index 0000000000..579738d57b --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_voice_call.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_voice_call_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_voice_call_solid.xml new file mode 100644 index 0000000000..428e2fa3d1 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_voice_call_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_volume_off.xml b/libraries/compound/src/main/res/drawable/ic_compound_volume_off.xml new file mode 100644 index 0000000000..e240c01b0b --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_volume_off.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_volume_off_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_volume_off_solid.xml new file mode 100644 index 0000000000..6c801e7a16 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_volume_off_solid.xml @@ -0,0 +1,10 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_volume_on.xml b/libraries/compound/src/main/res/drawable/ic_compound_volume_on.xml new file mode 100644 index 0000000000..453a2578e2 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_volume_on.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_volume_on_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_volume_on_solid.xml new file mode 100644 index 0000000000..232571342f --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_volume_on_solid.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_warning.xml b/libraries/compound/src/main/res/drawable/ic_compound_warning.xml new file mode 100644 index 0000000000..53b838ca56 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_warning.xml @@ -0,0 +1,13 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_web_browser.xml b/libraries/compound/src/main/res/drawable/ic_compound_web_browser.xml new file mode 100644 index 0000000000..baaf15c059 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_web_browser.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_windows.xml b/libraries/compound/src/main/res/drawable/ic_compound_windows.xml new file mode 100644 index 0000000000..37a3915dc8 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_windows.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_workspace.xml b/libraries/compound/src/main/res/drawable/ic_compound_workspace.xml new file mode 100644 index 0000000000..3871fde4b4 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_workspace.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_workspace_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_workspace_solid.xml new file mode 100644 index 0000000000..51c5c9f9cb --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_workspace_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/AvatarColorsTest.kt b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/AvatarColorsTest.kt new file mode 100644 index 0000000000..015cd5341c --- /dev/null +++ b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/AvatarColorsTest.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2023, 2025 New Vector 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.compound.screenshot + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.github.takahirom.roborazzi.captureRoboImage +import io.element.android.compound.screenshot.utils.screenshotFile +import io.element.android.compound.theme.AvatarColorsPreviewDark +import io.element.android.compound.theme.AvatarColorsPreviewLight +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +@RunWith(AndroidJUnit4::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class AvatarColorsTest { + @Test + @Config(sdk = [35], qualifiers = "xxhdpi") + fun screenshots() { + captureRoboImage(file = screenshotFile("Avatar Colors - Light.png")) { + AvatarColorsPreviewLight() + } + captureRoboImage(file = screenshotFile("Avatar Colors - Dark.png")) { + AvatarColorsPreviewDark() + } + } +} diff --git a/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/CompoundIconTest.kt b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/CompoundIconTest.kt new file mode 100644 index 0000000000..9ae73a6205 --- /dev/null +++ b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/CompoundIconTest.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2023, 2025 New Vector 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.compound.screenshot + +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.github.takahirom.roborazzi.captureRoboImage +import io.element.android.compound.previews.IconsCompoundPreviewDark +import io.element.android.compound.previews.IconsCompoundPreviewLight +import io.element.android.compound.previews.IconsCompoundPreviewRtl +import io.element.android.compound.previews.IconsPreview +import io.element.android.compound.screenshot.utils.screenshotFile +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import kotlinx.collections.immutable.toImmutableList +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +@RunWith(AndroidJUnit4::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class CompoundIconTest { + @Test + @Config(sdk = [35], qualifiers = "w1024dp-h2048dp") + fun screenshots() { + captureRoboImage(file = screenshotFile("Compound Icons - Light.png")) { + IconsCompoundPreviewLight() + } + captureRoboImage(file = screenshotFile("Compound Icons - Rtl.png")) { + IconsCompoundPreviewRtl() + } + captureRoboImage(file = screenshotFile("Compound Icons - Dark.png")) { + IconsCompoundPreviewDark() + } + captureRoboImage(file = screenshotFile("Compound Vector Icons - Light.png")) { + val content: List<@Composable ColumnScope.() -> Unit> = CompoundIcons.all.map { + @Composable { Icon(imageVector = it, contentDescription = null) } + } + ElementTheme { + IconsPreview( + title = "Compound Vector Icons", + content = content.toImmutableList() + ) + } + } + captureRoboImage(file = screenshotFile("Compound Vector Icons - Dark.png")) { + val content: List<@Composable ColumnScope.() -> Unit> = CompoundIcons.all.map { + @Composable { Icon(imageVector = it, contentDescription = null) } + } + ElementTheme(darkTheme = true) { + IconsPreview( + title = "Compound Vector Icons", + content = content.toImmutableList() + ) + } + } + } +} diff --git a/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/CompoundTypographyTest.kt b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/CompoundTypographyTest.kt new file mode 100644 index 0000000000..2d50e6287d --- /dev/null +++ b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/CompoundTypographyTest.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2023, 2025 New Vector 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.compound.screenshot + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.github.takahirom.roborazzi.captureRoboImage +import io.element.android.compound.screenshot.utils.screenshotFile +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.TypographyTokens +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +@RunWith(AndroidJUnit4::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class CompoundTypographyTest { + @Test + @Config(sdk = [35], qualifiers = "h2048dp-xxhdpi") + fun screenshots() { + captureRoboImage(file = screenshotFile("Compound Typography.png")) { + ElementTheme { + Surface { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + with(TypographyTokens) { + TypographyTokenPreview(fontHeadingXlBold, "Heading XL Bold") + TypographyTokenPreview(fontHeadingXlRegular, "Heading XL Regular") + TypographyTokenPreview(fontHeadingLgBold, "Heading LG Bold") + TypographyTokenPreview(fontHeadingLgRegular, "Heading LG Regular") + TypographyTokenPreview(fontHeadingMdBold, "Heading MD Bold") + TypographyTokenPreview(fontHeadingMdRegular, "Heading MD Regular") + TypographyTokenPreview(fontHeadingSmMedium, "Heading SM Medium") + TypographyTokenPreview(fontHeadingSmRegular, "Heading SM Regular") + TypographyTokenPreview(fontBodyLgMedium, "Body LG Medium") + TypographyTokenPreview(fontBodyLgRegular, "Body LG Regular") + TypographyTokenPreview(fontBodyMdMedium, "Body MD Medium") + TypographyTokenPreview(fontBodyMdRegular, "Body MD Regular") + TypographyTokenPreview(fontBodySmMedium, "Body SM Medium") + TypographyTokenPreview(fontBodySmRegular, "Body SM Regular") + TypographyTokenPreview(fontBodyXsMedium, "Body XS Medium") + TypographyTokenPreview(fontBodyXsRegular, "Body XS Regular") + } + } + } + } + } + } + + @Composable + private fun TypographyTokenPreview(style: TextStyle, text: String) { + Text(text = text, style = style) + } +} diff --git a/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/ForcedDarkElementThemeTest.kt b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/ForcedDarkElementThemeTest.kt new file mode 100644 index 0000000000..341b7cb650 --- /dev/null +++ b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/ForcedDarkElementThemeTest.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2023, 2025 New Vector 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.compound.screenshot + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.github.takahirom.roborazzi.captureRoboImage +import io.element.android.compound.screenshot.utils.screenshotFile +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.theme.ForcedDarkElementTheme +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +@RunWith(AndroidJUnit4::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class ForcedDarkElementThemeTest { + @Test + @Config(sdk = [35], qualifiers = "xxhdpi") + fun screenshots() { + captureRoboImage(file = screenshotFile("ForcedDarkElementTheme.png")) { + ElementTheme { + Surface { + Column( + modifier = Modifier.padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + Text(text = "Outside") + ForcedDarkElementTheme { + Surface { + Box(modifier = Modifier.fillMaxSize()) { + Text(text = "Inside ForcedDarkElementTheme", modifier = Modifier.align(Alignment.Center)) + } + } + } + } + } + } + } + } +} diff --git a/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/LegacyColorsTest.kt b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/LegacyColorsTest.kt new file mode 100644 index 0000000000..ecfbbe81cd --- /dev/null +++ b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/LegacyColorsTest.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2023, 2025 New Vector 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.compound.screenshot + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.github.takahirom.roborazzi.captureRoboImage +import io.element.android.compound.previews.ColorPreview +import io.element.android.compound.screenshot.utils.screenshotFile +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.theme.LinkColor +import io.element.android.compound.theme.SnackBarLabelColorDark +import io.element.android.compound.theme.SnackBarLabelColorLight +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +@RunWith(AndroidJUnit4::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class LegacyColorsTest { + @Test + @Config(sdk = [35], qualifiers = "xxhdpi") + fun screenshots() { + captureRoboImage(file = screenshotFile("Legacy Colors.png")) { + ElementTheme { + Surface { + Column(modifier = Modifier.padding(16.dp)) { + Text(text = "Legacy Colors") + Spacer(modifier = Modifier.height(10.dp)) + LegacyColorPreview( + color = LinkColor, + name = "Link" + ) + LegacyColorPreview( + color = SnackBarLabelColorLight, + name = "SnackBar Label - Light" + ) + LegacyColorPreview( + color = SnackBarLabelColorDark, + name = "SnackBar Label - Dark" + ) + } + } + } + } + } + + @Composable + private fun LegacyColorPreview(color: Color, name: String) { + ColorPreview( + backgroundColor = Color.White, + foregroundColor = Color.Black, + name = name, + color = color + ) + } +} diff --git a/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/MaterialColorSchemeTest.kt b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/MaterialColorSchemeTest.kt new file mode 100644 index 0000000000..7542eaa4fc --- /dev/null +++ b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/MaterialColorSchemeTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright 2023, 2025 New Vector 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.compound.screenshot + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.github.takahirom.roborazzi.captureRoboImage +import io.element.android.compound.screenshot.utils.screenshotFile +import io.element.android.compound.theme.ColorsSchemeDarkHcPreview +import io.element.android.compound.theme.ColorsSchemeDarkPreview +import io.element.android.compound.theme.ColorsSchemeLightHcPreview +import io.element.android.compound.theme.ColorsSchemeLightPreview +import io.element.android.compound.theme.ElementTheme +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +@RunWith(AndroidJUnit4::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class MaterialColorSchemeTest { + @Test + @Config(sdk = [35], qualifiers = "h2048dp-xhdpi") + fun screenshots() { + captureRoboImage(file = screenshotFile("Material3 Colors - Light.png")) { + ElementTheme { + Surface { + Column(modifier = Modifier.padding(16.dp)) { + Text( + text = "M3 Light colors", + style = TextStyle.Default.copy(fontSize = 18.sp), + ) + Spacer(modifier = Modifier.height(12.dp)) + ColorsSchemeLightPreview() + } + } + } + } + captureRoboImage(file = screenshotFile("Material3 Colors - Light HC.png")) { + ElementTheme { + Surface { + Column(modifier = Modifier.padding(16.dp)) { + Text( + text = "M3 Light HC colors", + style = TextStyle.Default.copy(fontSize = 18.sp), + ) + Spacer(modifier = Modifier.height(12.dp)) + ColorsSchemeLightHcPreview() + } + } + } + } + captureRoboImage(file = screenshotFile("Material3 Colors - Dark.png")) { + ElementTheme { + Surface { + Column(modifier = Modifier.padding(16.dp)) { + Text( + text = "M3 Dark colors", + style = TextStyle.Default.copy(fontSize = 18.sp), + ) + Spacer(modifier = Modifier.height(12.dp)) + ColorsSchemeDarkPreview() + } + } + } + } + captureRoboImage(file = screenshotFile("Material3 Colors - Dark HC.png")) { + ElementTheme { + Surface { + Column(modifier = Modifier.padding(16.dp)) { + Text( + text = "M3 Dark HC colors", + style = TextStyle.Default.copy(fontSize = 18.sp), + ) + Spacer(modifier = Modifier.height(12.dp)) + ColorsSchemeDarkHcPreview() + } + } + } + } + } +} diff --git a/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/MaterialTextTest.kt b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/MaterialTextTest.kt new file mode 100644 index 0000000000..764d4de77c --- /dev/null +++ b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/MaterialTextTest.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2025 New Vector 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.compound.screenshot + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.github.takahirom.roborazzi.captureRoboImage +import io.element.android.compound.screenshot.utils.screenshotFile +import io.element.android.compound.theme.MaterialTextPreview +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +@RunWith(AndroidJUnit4::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class MaterialTextTest { + @Test + @Config(sdk = [35], qualifiers = "w480dp-h1200dp-xxhdpi") + fun screenshots() { + captureRoboImage(file = screenshotFile("MaterialText Colors.png")) { + MaterialTextPreview() + } + } +} diff --git a/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/MaterialTypographyTest.kt b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/MaterialTypographyTest.kt new file mode 100644 index 0000000000..4f77a070ed --- /dev/null +++ b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/MaterialTypographyTest.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2023, 2025 New Vector 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.compound.screenshot + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.github.takahirom.roborazzi.captureRoboImage +import io.element.android.compound.previews.TypographyPreview +import io.element.android.compound.screenshot.utils.screenshotFile +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +@RunWith(AndroidJUnit4::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class MaterialTypographyTest { + @Test + @Config(sdk = [35], qualifiers = "h2048dp-xxhdpi") + fun screenshots() { + captureRoboImage(file = screenshotFile("Material Typography.png")) { + TypographyPreview() + } + } +} diff --git a/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/MaterialYouThemeTest.kt b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/MaterialYouThemeTest.kt new file mode 100644 index 0000000000..694d2c9fc6 --- /dev/null +++ b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/MaterialYouThemeTest.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2023, 2025 New Vector 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.compound.screenshot + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.github.takahirom.roborazzi.captureRoboImage +import io.element.android.compound.previews.ColorsSchemePreview +import io.element.android.compound.screenshot.utils.screenshotFile +import io.element.android.compound.theme.ElementTheme +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +@RunWith(AndroidJUnit4::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class MaterialYouThemeTest { + @Test + @Config(sdk = [35], qualifiers = "h2048dp-xhdpi") + fun screenshots() { + captureRoboImage(file = screenshotFile("MaterialYou Theme - Light.png")) { + ElementTheme(dynamicColor = true) { + Surface { + Column( + modifier = Modifier.padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + Text(text = "Material You Theme - Light") + Spacer(modifier = Modifier.height(12.dp)) + ColorsSchemePreview(Color.White, Color.Black, ElementTheme.materialColors) + } + } + } + } + captureRoboImage(file = screenshotFile("MaterialYou Theme - Dark.png")) { + ElementTheme(dynamicColor = true, darkTheme = true) { + Surface { + Column( + modifier = Modifier.padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + Text(text = "Material You Theme - Dark") + Spacer(modifier = Modifier.height(12.dp)) + ColorsSchemePreview(Color.White, Color.Black, ElementTheme.materialColors) + } + } + } + } + } +} diff --git a/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/SemanticColorsTest.kt b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/SemanticColorsTest.kt new file mode 100644 index 0000000000..7e5fabd96a --- /dev/null +++ b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/SemanticColorsTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2023, 2025 New Vector 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.compound.screenshot + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.github.takahirom.roborazzi.captureRoboImage +import io.element.android.compound.previews.CompoundSemanticColorsDark +import io.element.android.compound.previews.CompoundSemanticColorsDarkHc +import io.element.android.compound.previews.CompoundSemanticColorsLight +import io.element.android.compound.previews.CompoundSemanticColorsLightHc +import io.element.android.compound.screenshot.utils.screenshotFile +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +@RunWith(AndroidJUnit4::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class SemanticColorsTest { + @Config(sdk = [35], qualifiers = "h2000dp-xhdpi") + @Test + fun screenshots() { + captureRoboImage(file = screenshotFile("Compound Semantic Colors - Light.png")) { + CompoundSemanticColorsLight() + } + + captureRoboImage(file = screenshotFile("Compound Semantic Colors - Light HC.png")) { + CompoundSemanticColorsLightHc() + } + + captureRoboImage(file = screenshotFile("Compound Semantic Colors - Dark.png")) { + CompoundSemanticColorsDark() + } + + captureRoboImage(file = screenshotFile("Compound Semantic Colors - Dark HC.png")) { + CompoundSemanticColorsDarkHc() + } + } +} diff --git a/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/utils/ScreenshotUtils.kt b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/utils/ScreenshotUtils.kt new file mode 100644 index 0000000000..3d4c9b3824 --- /dev/null +++ b/libraries/compound/src/test/kotlin/io/element/android/compound/screenshot/utils/ScreenshotUtils.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2023, 2025 New Vector 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.compound.screenshot.utils + +import java.io.File + +/** + * Returns a [File] object for a screenshot with the given [filename]. + * This is to ensure we have a consistent location for all screenshots. + */ +fun screenshotFile(filename: String): File { + return File("screenshots", filename) +} diff --git a/libraries/compound/src/test/kotlin/io/element/android/compound/theme/ThemeTest.kt b/libraries/compound/src/test/kotlin/io/element/android/compound/theme/ThemeTest.kt new file mode 100644 index 0000000000..17cb2425dc --- /dev/null +++ b/libraries/compound/src/test/kotlin/io/element/android/compound/theme/ThemeTest.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2025 New Vector 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.compound.theme + +import android.content.res.Configuration +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.LocalConfiguration +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class ThemeTest { + @Test + fun `isDark for System dark returns true`() { + `isDark for System`( + uiMode = Configuration.UI_MODE_NIGHT_YES, + expected = true, + ) + } + + @Test + fun `isDark for System light return false`() { + `isDark for System`( + uiMode = Configuration.UI_MODE_NIGHT_NO, + expected = false, + ) + } + + fun `isDark for System`( + uiMode: Int, + expected: Boolean, + ) = runTest { + moleculeFlow(RecompositionMode.Immediate) { + var result: Boolean? = null + CompositionLocalProvider( + // Let set the system to dark + LocalConfiguration provides Configuration().apply { + this.uiMode = uiMode + }, + ) { + result = Theme.System.isDark() + } + result + }.test { + assertThat(awaitItem()).isEqualTo(expected) + } + } + + @Test + fun `isDark for Light returns false`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + Theme.Light.isDark() + }.test { + assertThat(awaitItem()).isFalse() + } + } + + @Test + fun `isDark for Dark returns true`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + Theme.Dark.isDark() + }.test { + assertThat(awaitItem()).isTrue() + } + } +} diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/coroutine/CoroutineDispatchers.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/coroutine/CoroutineDispatchers.kt index 94bfa8e324..606fe20158 100644 --- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/coroutine/CoroutineDispatchers.kt +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/coroutine/CoroutineDispatchers.kt @@ -8,9 +8,18 @@ package io.element.android.libraries.core.coroutine import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers data class CoroutineDispatchers( val io: CoroutineDispatcher, val computation: CoroutineDispatcher, val main: CoroutineDispatcher, -) +) { + companion object { + val Default = CoroutineDispatchers( + io = Dispatchers.IO, + computation = Dispatchers.Default, + main = Dispatchers.Main, + ) + } +} diff --git a/libraries/dateformatter/impl/src/main/res/values-hu/translations.xml b/libraries/dateformatter/impl/src/main/res/values-hu/translations.xml index 33778d84f1..0f4e767bf4 100644 --- a/libraries/dateformatter/impl/src/main/res/values-hu/translations.xml +++ b/libraries/dateformatter/impl/src/main/res/values-hu/translations.xml @@ -1,5 +1,5 @@ - "%1$s itt: %2$s" + "%1$s, %2$s" "Ebben a hónapban" diff --git a/libraries/designsystem/build.gradle.kts b/libraries/designsystem/build.gradle.kts index c2eec20b8c..3983317055 100644 --- a/libraries/designsystem/build.gradle.kts +++ b/libraries/designsystem/build.gradle.kts @@ -27,7 +27,7 @@ android { } dependencies { - api(libs.compound) + api(projects.libraries.compound) implementation(libs.androidx.compose.material3.windowsizeclass) implementation(libs.androidx.compose.material3.adaptive) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/BetaLabel.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/BetaLabel.kt new file mode 100644 index 0000000000..2600b83561 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/BetaLabel.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2025 New Vector 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.atomic.atoms + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.ui.strings.CommonStrings + +@Composable +fun BetaLabel( + modifier: Modifier = Modifier, +) { + val shape = RoundedCornerShape(size = 6.dp) + Text( + modifier = modifier + .border( + width = 1.dp, + color = ElementTheme.colors.borderInfoSubtle, + shape = shape, + ) + .background( + color = ElementTheme.colors.bgInfoSubtle, + shape = shape, + ) + .padding(horizontal = 8.dp, vertical = 4.dp), + text = stringResource(CommonStrings.common_beta).uppercase(), + style = ElementTheme.typography.fontBodySmMedium, + color = ElementTheme.colors.textInfoPrimary, + ) +} + +@PreviewsDayNight +@Composable +internal fun BetaLabelPreview() = ElementPreview { + BetaLabel() +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitleSubtitleMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitleSubtitleMolecule.kt index a89a21adbd..e38070e09f 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitleSubtitleMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitleSubtitleMolecule.kt @@ -7,7 +7,9 @@ package io.element.android.libraries.designsystem.atomic.molecules +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -20,6 +22,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.libraries.designsystem.atomic.atoms.BetaLabel import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -32,6 +35,7 @@ import io.element.android.libraries.designsystem.theme.components.Text * @param subTitle the subtitle to display * @param iconStyle the style of the [BigIcon] to display * @param modifier the modifier to apply to this layout + * @param showBetaLabel whether to show a "BETA" label next to the title */ @Composable fun IconTitleSubtitleMolecule( @@ -39,6 +43,7 @@ fun IconTitleSubtitleMolecule( subTitle: String?, iconStyle: BigIcon.Style, modifier: Modifier = Modifier, + showBetaLabel: Boolean = false, ) { Column(modifier) { BigIcon( @@ -46,17 +51,26 @@ fun IconTitleSubtitleMolecule( style = iconStyle, ) Spacer(modifier = Modifier.height(16.dp)) - Text( - text = title, - modifier = Modifier - .fillMaxWidth() - .semantics { - heading() - }, - textAlign = TextAlign.Center, - style = ElementTheme.typography.fontHeadingMdBold, - color = ElementTheme.colors.textPrimary, - ) + FlowRow( + modifier = Modifier.fillMaxWidth(), + itemVerticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterHorizontally), + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + text = title, + modifier = Modifier + .semantics { + heading() + }, + textAlign = TextAlign.Center, + style = ElementTheme.typography.fontHeadingMdBold, + color = ElementTheme.colors.textPrimary, + ) + if (showBetaLabel) { + BetaLabel() + } + } if (subTitle != null) { Spacer(Modifier.height(8.dp)) Text( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/BigIcon.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/BigIcon.kt index f469555ee4..78a4d32e44 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/BigIcon.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/BigIcon.kt @@ -53,11 +53,13 @@ object BigIcon { * @param vectorIcon the [ImageVector] to display * @param contentDescription the content description of the icon, if any. It defaults to `null` * @param useCriticalTint whether the icon and background should be rendered using critical tint + * @param usePrimaryTint whether the icon should be rendered using primary tint */ data class Default( val vectorIcon: ImageVector, val contentDescription: String? = null, val useCriticalTint: Boolean = false, + val usePrimaryTint: Boolean = false, ) : Style /** @@ -143,6 +145,8 @@ object BigIcon { val iconTint = when (style) { is Style.Default -> if (style.useCriticalTint) { ElementTheme.colors.iconCriticalPrimary + } else if (style.usePrimaryTint) { + ElementTheme.colors.iconPrimary } else { ElementTheme.colors.iconSecondary } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ClickableLinkText.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ClickableLinkText.kt index 3638556da3..6c6d6d398e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ClickableLinkText.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ClickableLinkText.kt @@ -20,6 +20,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.text.AnnotatedString @@ -51,6 +52,7 @@ fun ClickableLinkText( onClick: () -> Unit = {}, onLongClick: () -> Unit = {}, style: TextStyle = LocalTextStyle.current, + color: Color = Color.Unspecified, inlineContent: ImmutableMap = persistentMapOf(), ) { ClickableLinkText( @@ -62,6 +64,7 @@ fun ClickableLinkText( onClick = onClick, onLongClick = onLongClick, style = style, + color = color, inlineContent = inlineContent, ) } @@ -76,6 +79,7 @@ fun ClickableLinkText( onClick: () -> Unit = {}, onLongClick: () -> Unit = {}, style: TextStyle = LocalTextStyle.current, + color: Color = Color.Unspecified, inlineContent: ImmutableMap = persistentMapOf(), ) { @Suppress("NAME_SHADOWING") @@ -126,6 +130,7 @@ fun ClickableLinkText( text = annotatedString, modifier = modifier.then(pressIndicator), style = style, + color = color, onTextLayout = { layoutResult.value = it }, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/SimpleModalBottomSheet.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/SimpleModalBottomSheet.kt new file mode 100644 index 0000000000..5b87b1171b --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/SimpleModalBottomSheet.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2025 New Vector 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.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +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.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet +import io.element.android.libraries.designsystem.theme.components.Text + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SimpleModalBottomSheet( + title: String, + onDismiss: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit, +) { + ModalBottomSheet( + onDismissRequest = onDismiss, + modifier = modifier, + sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + ) { + Text( + title, + style = ElementTheme.typography.fontBodyLgMedium, + color = ElementTheme.colors.textPrimary, + ) + Spacer(Modifier.height(8.dp)) + content() + } + } +} + +@PreviewsDayNight +@Composable +internal fun SimpleModalBottomSheetPreview() = ElementPreview { + SimpleModalBottomSheet(title = "A title", onDismiss = {}) { + Text( + text = LoremIpsum(20).values.first(), + color = ElementTheme.colors.textSecondary, + style = ElementTheme.typography.fontBodyMdRegular, + ) + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt index db7ef62483..d3a9ed3107 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt @@ -8,12 +8,13 @@ package io.element.android.libraries.designsystem.components.avatar import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.components.avatar.internal.RoomAvatar @@ -21,8 +22,8 @@ import io.element.android.libraries.designsystem.components.avatar.internal.Spac import io.element.android.libraries.designsystem.components.avatar.internal.UserAvatar import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables +import kotlinx.collections.immutable.persistentListOf @Composable fun Avatar( @@ -64,18 +65,54 @@ fun Avatar( @Preview(group = PreviewGroup.Avatars) @Composable -internal fun AvatarPreview(@PreviewParameter(AvatarDataProvider::class) avatarData: AvatarData) = - ElementThemedPreview( - drawableFallbackForImages = CommonDrawables.sample_avatar, +internal fun AvatarPreview() = ElementThemedPreview( + drawableFallbackForImages = CommonDrawables.sample_background, +) { + Column( + modifier = Modifier.padding(4.dp), + verticalArrangement = Arrangement.spacedBy(4.dp), ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(16.dp) - ) { - Avatar( - avatarData = avatarData, - avatarType = AvatarType.User, - ) - Text(text = avatarData.size.name + " " + avatarData.size.dp) + listOf( + anAvatarData(size = AvatarSize.UserListItem), + anAvatarData(size = AvatarSize.UserListItem, name = null), + anAvatarData(size = AvatarSize.UserListItem, url = "aUrl"), + ).forEach { avatarData -> + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Avatar( + avatarData = avatarData, + avatarType = AvatarType.User, + ) + Avatar( + avatarData = avatarData, + avatarType = AvatarType.Room(isTombstoned = false), + ) + Avatar( + avatarData = avatarData, + avatarType = AvatarType.Room( + heroes = persistentListOf( + anAvatarData("@carol:server.org", "Carol", size = AvatarSize.UserListItem), + anAvatarData("@david:server.org", "David", size = AvatarSize.UserListItem), + anAvatarData("@eve:server.org", "Eve", size = AvatarSize.UserListItem), + anAvatarData("@justin:server.org", "Justin", size = AvatarSize.UserListItem), + ) + ) + ) + Avatar( + avatarData = avatarData, + avatarType = AvatarType.Room(isTombstoned = true), + ) + Avatar( + avatarData = avatarData, + avatarType = AvatarType.Space(isTombstoned = false), + ) + Avatar( + avatarData = avatarData, + avatarType = AvatarType.Space(isTombstoned = true), + ) + } } } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt index 7ccebd2082..2bc497f10d 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt @@ -7,22 +7,6 @@ package io.element.android.libraries.designsystem.components.avatar -import androidx.compose.ui.tooling.preview.PreviewParameterProvider - -open class AvatarDataProvider : PreviewParameterProvider { - override val values: Sequence - get() = AvatarSize.entries - .asSequence() - .map { - sequenceOf( - anAvatarData(size = it), - anAvatarData(size = it, name = null), - anAvatarData(size = it, url = "aUrl"), - ) - } - .flatten() -} - fun anAvatarData( // Let's the id not start with a 'a'. id: String = "@id_of_alice:server.org", diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt index 72cf62c76a..31c569c870 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt @@ -72,4 +72,7 @@ enum class AvatarSize(val dp: Dp) { RoomPreviewHeader(64.dp), RoomPreviewInviter(56.dp), SpaceMember(24.dp), + LeaveSpaceRoom(32.dp), + + AccountItem(32.dp), } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/internal/AvatarCluster.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/internal/AvatarCluster.kt index c100988053..1c8ac81e64 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/internal/AvatarCluster.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/internal/AvatarCluster.kt @@ -27,7 +27,7 @@ import io.element.android.libraries.designsystem.components.avatar.avatarShape import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import java.util.Collections import kotlin.math.PI import kotlin.math.cos @@ -134,7 +134,7 @@ internal fun AvatarClusterPreview() = ElementThemedPreview { ) { for (ngOfAvatars in 1..5) { AvatarCluster( - avatars = List(ngOfAvatars) { anAvatarData(it) }.toPersistentList(), + avatars = List(ngOfAvatars) { anAvatarData(it) }.toImmutableList(), avatarType = avatarType, ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/internal/RoomAvatar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/internal/RoomAvatar.kt index 9fd2ff8cca..a9d1e5a95e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/internal/RoomAvatar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/internal/RoomAvatar.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.unit.Dp import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.components.avatar.avatarShape +import kotlinx.collections.immutable.toImmutableList @Composable internal fun RoomAvatar( @@ -44,8 +45,9 @@ internal fun RoomAvatar( } else -> { AvatarCluster( - avatars = avatarType.heroes, - // Note: even for a room avatar, we use UserAvatarType here to display the avatar of heroes + // Keep only the first hero for now + avatars = avatarType.heroes.take(1).toImmutableList(), + // Note: even for a room avatar, we use AvatarType.User here to display the avatar of heroes avatarType = AvatarType.User, modifier = modifier, hideAvatarImages = hideAvatarImage, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/FakeWaveformFactory.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/FakeWaveformFactory.kt index c9e8876aad..3264f55754 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/FakeWaveformFactory.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/FakeWaveformFactory.kt @@ -8,7 +8,7 @@ package io.element.android.libraries.designsystem.components.media import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlin.random.Random /** @@ -21,5 +21,5 @@ import kotlin.random.Random fun createFakeWaveform(length: Int = 1000): ImmutableList { val random = Random(seed = 2) return List(length) { random.nextFloat() } - .toPersistentList() + .toImmutableList() } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/WaveformPlaybackView.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/WaveformPlaybackView.kt index 767b56410f..5fe722d447 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/WaveformPlaybackView.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/media/WaveformPlaybackView.kt @@ -39,7 +39,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlin.math.roundToInt private const val DEFAULT_GRAPHICS_LAYER_ALPHA: Float = 0.99F @@ -187,14 +187,14 @@ internal fun WaveformPlaybackViewPreview() = ElementPreview { showCursor = false, playbackProgress = 0.5f, onSeek = {}, - waveform = aWaveForm().toPersistentList(), + waveform = aWaveForm().toImmutableList(), ) WaveformPlaybackView( modifier = Modifier.height(34.dp), showCursor = true, playbackProgress = 0.5f, onSeek = {}, - waveform = List(1024) { it / 1024f }.toPersistentList(), + waveform = List(1024) { it / 1024f }.toImmutableList(), ) } } @@ -215,7 +215,7 @@ private fun ImmutableList.normalisedData(maxSamplesCount: Int): Immutable this } - return result.toPersistentList() + return result.toImmutableList() } fun aWaveForm(): List { diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt index 993b2c32c4..e2da9bdcc0 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt @@ -28,14 +28,14 @@ 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.Text import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList internal class CompoundIconChunkProvider : PreviewParameterProvider { override val values: Sequence get() { val chunks = CompoundIcons.allResIds.chunked(36) return chunks.mapIndexed { index, chunk -> - IconChunk(index = index + 1, total = chunks.size, icons = chunk.toPersistentList()) + IconChunk(index = index + 1, total = chunks.size, icons = chunk.toImmutableList()) } .asSequence() } @@ -46,7 +46,7 @@ internal class OtherIconChunkProvider : PreviewParameterProvider { get() { val chunks = iconsOther.chunked(36) return chunks.mapIndexed { index, chunk -> - IconChunk(index = index + 1, total = chunks.size, icons = chunk.toPersistentList()) + IconChunk(index = index + 1, total = chunks.size, icons = chunk.toImmutableList()) } .asSequence() } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Icon.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Icon.kt index 3867e37c4a..f4487b34fa 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Icon.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Icon.kt @@ -8,6 +8,11 @@ package io.element.android.libraries.designsystem.theme.components import androidx.annotation.DrawableRes +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.material3.LocalContentColor import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -15,9 +20,12 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup @@ -134,3 +142,24 @@ fun Icon( internal fun IconImageVectorPreview() = ElementThemedPreview { Icon(imageVector = CompoundIcons.Close(), contentDescription = null) } + +@Preview(group = PreviewGroup.Icons) +@Composable +internal fun AllIconsPreview() = ElementPreview { + LazyVerticalGrid( + modifier = Modifier.fillMaxWidth(), + columns = GridCells.Adaptive(32.dp), + contentPadding = PaddingValues(2.dp), + verticalArrangement = Arrangement.spacedBy(2.dp), + horizontalArrangement = Arrangement.spacedBy(2.dp) + ) { + CompoundIcons.allResIds.forEach { icon -> + item { + Icon( + painter = painterResource(icon), + contentDescription = null, + ) + } + } + } +} diff --git a/libraries/di/build.gradle.kts b/libraries/di/build.gradle.kts index a7bd4f2c44..1b2b04d880 100644 --- a/libraries/di/build.gradle.kts +++ b/libraries/di/build.gradle.kts @@ -11,6 +11,5 @@ plugins { } dependencies { - api(libs.inject) api(libs.metro.runtime) } diff --git a/libraries/eventformatter/impl/src/main/res/values-bg/translations.xml b/libraries/eventformatter/impl/src/main/res/values-bg/translations.xml index b1f3fe1f11..9bfcdfe6a6 100644 --- a/libraries/eventformatter/impl/src/main/res/values-bg/translations.xml +++ b/libraries/eventformatter/impl/src/main/res/values-bg/translations.xml @@ -26,7 +26,11 @@ "%1$s получи достъп до %2$s" "Вие позволихте на %1$s да се присъедини" "Вие поискахте да се присъедините" + "%1$s отхвърли заявката на %2$s за присъединяване" + "Вие отхвърлихте заявката на %1$s за присъединяване" + "%1$s отхвърли вашата заявка за присъединяване" "%1$s вече не се интересува от присъединяване" + "Вие отменихте заявката си за присъединяване" "%1$s напусна стаята" "Вие напуснахте стаята" "%1$s промени името на стаята на: %2$s" @@ -39,14 +43,19 @@ "Вие променихте закачените съобщения" "%1$s закачи съобщение" "Вие закачихте съобщение" + "%1$s откачи съобщение" + "Вие откачихте съобщение" "%1$s отхвърли поканата" "Вие отхвърлихте поканата" "%1$s премахна %2$s" "Вие премахнахте %1$s" "%1$s изпрати покана на %2$s за присъединяване към стаята" "Вие изпратихте покана на %1$s за присъединяване към стаята" + "%1$s отмени поканата на %2$s за присъединяване към стаята" + "Вие отменихте поканата на %1$s за присъединяване към стаята" "%1$s промени темата на: %2$s" "Вие променихте темата на: %1$s" "%1$s премахна темата на стаята" "Вие премахнахте темата на стаята" + "%1$s направи неизвестна промяна в членството си" diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/Feature.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/Feature.kt index af1031450e..18a9ea1bc3 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/Feature.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/Feature.kt @@ -36,4 +36,10 @@ interface Feature { * If true: the feature is finished, it will not appear in the developer options screen. */ val isFinished: Boolean + + /** + * Whether the feature is only available in Labs (and not in developer options). + * Feature flags that set this to `true` can be enabled by any users, not only those that have enabled developer mode. + */ + val isInLabs: Boolean } diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlagService.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlagService.kt index 3bd94c9bd7..1358909771 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlagService.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlagService.kt @@ -33,4 +33,9 @@ interface FeatureFlagService { * is registered */ suspend fun setFeatureEnabled(feature: Feature, enabled: Boolean): Boolean + + /** + * @return the list of available (not finished) features that can be toggled. + */ + fun getAvailableFeatures(): List } diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt index 43895bce16..cb908dbe5e 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt @@ -19,6 +19,7 @@ enum class FeatureFlags( override val description: String? = null, override val defaultValue: (BuildMeta) -> Boolean, override val isFinished: Boolean, + override val isInLabs: Boolean = false, ) : Feature { RoomDirectorySearch( key = "feature.roomdirectorysearch", @@ -71,8 +72,7 @@ enum class FeatureFlags( Space( key = "feature.space", title = "Spaces", - description = "Spaces are under active development, only developers should enable this flag for now.", - defaultValue = { false }, + defaultValue = { true }, isFinished = false, ), PrintLogsToLogcat( @@ -99,5 +99,14 @@ enum class FeatureFlags( description = "Renders thread messages as a dedicated timeline. Restarting the app is required for this setting to fully take effect.", defaultValue = { false }, isFinished = false, - ) + isInLabs = true, + ), + MultiAccount( + key = "feature.multi_account", + title = "Multi accounts", + description = "Allow the application to connect to multiple accounts at the same time." + + "\n\nWARNING: this feature is EXPERIMENTAL and UNSTABLE.", + defaultValue = { false }, + isFinished = false, + ), } diff --git a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagService.kt b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagService.kt index 5bcbe93085..01c1e8a258 100644 --- a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagService.kt +++ b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagService.kt @@ -14,6 +14,7 @@ import dev.zacsweers.metro.SingleIn import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.featureflag.api.Feature import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf @@ -40,4 +41,8 @@ class DefaultFeatureFlagService( ?.let { true } ?: false } + + override fun getAvailableFeatures(): List { + return FeatureFlags.entries.filter { !it.isFinished } + } } diff --git a/libraries/featureflag/test/src/main/java/io/element/android/libraries/featureflag/test/FakeFeature.kt b/libraries/featureflag/test/src/main/java/io/element/android/libraries/featureflag/test/FakeFeature.kt new file mode 100644 index 0000000000..a141fbdd75 --- /dev/null +++ b/libraries/featureflag/test/src/main/java/io/element/android/libraries/featureflag/test/FakeFeature.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2025 New Vector 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.featureflag.test + +import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.featureflag.api.Feature + +data class FakeFeature( + override val key: String, + override val title: String, + override val description: String? = null, + override val defaultValue: (BuildMeta) -> Boolean = { false }, + override val isFinished: Boolean = false, + override val isInLabs: Boolean = false, +) : Feature diff --git a/libraries/featureflag/test/src/main/java/io/element/android/libraries/featureflag/test/FakeFeatureFlagService.kt b/libraries/featureflag/test/src/main/java/io/element/android/libraries/featureflag/test/FakeFeatureFlagService.kt index 695d3014bc..49fc095ed4 100644 --- a/libraries/featureflag/test/src/main/java/io/element/android/libraries/featureflag/test/FakeFeatureFlagService.kt +++ b/libraries/featureflag/test/src/main/java/io/element/android/libraries/featureflag/test/FakeFeatureFlagService.kt @@ -17,6 +17,7 @@ import kotlinx.coroutines.flow.MutableStateFlow class FakeFeatureFlagService( initialState: Map = emptyMap(), private val buildMeta: BuildMeta = aBuildMeta(), + var providedAvailableFeatures: List = emptyList(), ) : FeatureFlagService { private val enabledFeatures = initialState .mapValues { MutableStateFlow(it.value) } @@ -31,4 +32,8 @@ class FakeFeatureFlagService( override fun isFeatureEnabledFlow(feature: Feature): Flow { return enabledFeatures.getOrPut(feature.key) { MutableStateFlow(feature.defaultValue(buildMeta)) } } + + override fun getAvailableFeatures(): List { + return providedAvailableFeatures + } } diff --git a/libraries/featureflag/ui/src/main/kotlin/io/element/android/libraries/featureflag/ui/model/FeatureUiModel.kt b/libraries/featureflag/ui/src/main/kotlin/io/element/android/libraries/featureflag/ui/model/FeatureUiModel.kt index d6fa020f2f..f37e096c5b 100644 --- a/libraries/featureflag/ui/src/main/kotlin/io/element/android/libraries/featureflag/ui/model/FeatureUiModel.kt +++ b/libraries/featureflag/ui/src/main/kotlin/io/element/android/libraries/featureflag/ui/model/FeatureUiModel.kt @@ -7,9 +7,12 @@ package io.element.android.libraries.featureflag.ui.model +import io.element.android.libraries.designsystem.theme.components.IconSource + data class FeatureUiModel( val key: String, val title: String, val description: String?, + val icon: IconSource?, val isEnabled: Boolean ) diff --git a/libraries/featureflag/ui/src/main/kotlin/io/element/android/libraries/featureflag/ui/model/FeatureUiModelProvider.kt b/libraries/featureflag/ui/src/main/kotlin/io/element/android/libraries/featureflag/ui/model/FeatureUiModelProvider.kt index 4d515eb984..66697b447f 100644 --- a/libraries/featureflag/ui/src/main/kotlin/io/element/android/libraries/featureflag/ui/model/FeatureUiModelProvider.kt +++ b/libraries/featureflag/ui/src/main/kotlin/io/element/android/libraries/featureflag/ui/model/FeatureUiModelProvider.kt @@ -12,7 +12,7 @@ import kotlinx.collections.immutable.persistentListOf fun aFeatureUiModelList(): ImmutableList { return persistentListOf( - FeatureUiModel("key1", "Display State Events", "Show state events in the timeline", true), - FeatureUiModel("key2", "Display Room Events", null, false), + FeatureUiModel(key = "key1", title = "Display State Events", description = "Show state events in the timeline", icon = null, isEnabled = true), + FeatureUiModel(key = "key2", title = "Display Room Events", description = null, icon = null, isEnabled = false), ) } diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/CameraPositionState.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/CameraPositionState.kt index d3f9cf5971..b5b46ca847 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/CameraPositionState.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/CameraPositionState.kt @@ -31,9 +31,8 @@ import org.maplibre.android.maps.Projection */ @Composable public inline fun rememberCameraPositionState( - key: String? = null, crossinline init: CameraPositionState.() -> Unit = {} -): CameraPositionState = rememberSaveable(key = key, saver = CameraPositionState.Saver) { +): CameraPositionState = rememberSaveable(saver = CameraPositionState.Saver) { CameraPositionState().apply(init) } diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLibreMap.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLibreMap.kt index 5de213615e..f1ef0f0459 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLibreMap.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLibreMap.kt @@ -8,7 +8,7 @@ package io.element.android.libraries.maplibre.compose -import android.content.ComponentCallbacks +import android.content.ComponentCallbacks2 import android.content.Context import android.content.res.Configuration import android.os.Bundle @@ -235,11 +235,15 @@ private fun MapView.lifecycleObserver(previousState: MutableState val roomListService: RoomListService val spaceService: SpaceService - val mediaLoader: MatrixMediaLoader + val syncService: SyncService + val sessionVerificationService: SessionVerificationService + val pushersService: PushersService + val notificationService: NotificationService + val notificationSettingsService: NotificationSettingsService + val encryptionService: EncryptionService + val roomDirectoryService: RoomDirectoryService + val mediaPreviewService: MediaPreviewService + val matrixMediaLoader: MatrixMediaLoader val sessionCoroutineScope: CoroutineScope val ignoredUsersFlow: StateFlow> + val roomMembershipObserver: RoomMembershipObserver suspend fun getJoinedRoom(roomId: RoomId): JoinedRoom? suspend fun getRoom(roomId: RoomId): BaseRoom? suspend fun findDM(userId: UserId): Result @@ -68,14 +77,6 @@ interface MatrixClient { suspend fun joinRoom(roomId: RoomId): Result suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List): Result - fun syncService(): SyncService - fun sessionVerificationService(): SessionVerificationService - fun pushersService(): PushersService - fun notificationService(): NotificationService - fun notificationSettingsService(): NotificationSettingsService - fun encryptionService(): EncryptionService - fun roomDirectoryService(): RoomDirectoryService - fun mediaPreviewService(): MediaPreviewService suspend fun getCacheSize(): Long /** @@ -97,7 +98,6 @@ interface MatrixClient { suspend fun getUserProfile(): Result suspend fun getAccountManagementUrl(action: AccountManagementAction?): Result suspend fun uploadMedia(mimeType: String, data: ByteArray): Result - fun roomMembershipObserver(): RoomMembershipObserver /** * Get a room info flow for a given room ID. @@ -173,6 +173,16 @@ interface MatrixClient { * Returns the maximum file upload size allowed by the Matrix server. */ suspend fun getMaxFileUploadSize(): Result + + /** + * Returns the list of shared recent emoji reactions for this account. + */ + suspend fun getRecentEmojis(): Result> + + /** + * Adds an emoji to the list of recent emoji reactions for this account. + */ + suspend fun addRecentEmoji(emoji: String): Result } /** diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt index ef73edfaf5..03e8d57150 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.matrix.api.auth sealed class AuthenticationException(message: String) : Exception(message) { + class AccountAlreadyLoggedIn(userId: String) : AuthenticationException(userId) class InvalidServerName(message: String) : AuthenticationException(message) class SlidingSyncVersion(message: String) : AuthenticationException(message) class Oidc(message: String) : AuthenticationException(message) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt index 26a030d361..c833d46718 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt @@ -69,14 +69,6 @@ object MatrixPatterns { str matches PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER } - /** - * Tells if a string is a valid space id. This is an alias for [isRoomId] - * - * @param str the string to test - * @return true if the string is a valid space Id - */ - fun isSpaceId(str: String?) = isRoomId(str) - /** * Tells if a string is a valid room id. * diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/SpaceId.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/SpaceId.kt index 74074ed8e4..6db907bacd 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/SpaceId.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/SpaceId.kt @@ -7,23 +7,7 @@ package io.element.android.libraries.matrix.api.core -import io.element.android.libraries.androidutils.metadata.isInDebug -import java.io.Serializable - -@JvmInline -value class SpaceId(val value: String) : Serializable { - init { - if (isInDebug && !MatrixPatterns.isSpaceId(value)) { - error( - "`$value` is not a valid space id.\n" + - "Space ids are the same as room ids.\n" + - "Example space id: `!space_id:domain`." - ) - } - } - - override fun toString(): String = value -} +typealias SpaceId = RoomId /** * Value to use when no space is selected by the user. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt index 1635cce375..961178ee3e 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt @@ -17,6 +17,7 @@ interface EncryptionService { val recoveryStateStateFlow: StateFlow val enableRecoveryProgressStateFlow: StateFlow val isLastDevice: StateFlow + val hasDevicesToVerifyAgainst: StateFlow suspend fun enableBackups(): Result diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/RecoveryException.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/RecoveryException.kt index 70b335d4f9..7dffbf3653 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/RecoveryException.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/RecoveryException.kt @@ -11,6 +11,7 @@ import io.element.android.libraries.matrix.api.exception.ClientException sealed class RecoveryException(message: String) : Exception(message) { class SecretStorage(message: String) : RecoveryException(message) + class Import(message: String) : RecoveryException(message) data object BackupExistsOnServer : RecoveryException("BackupExistsOnServer") data class Client(val exception: ClientException) : RecoveryException(exception.message ?: "Unknown error") } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt index 1f9dd8af8d..1f5f39dee7 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.matrix.api.permalink import android.net.Uri +import android.os.Parcelable import androidx.compose.runtime.Immutable import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId @@ -15,13 +16,15 @@ import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.core.UserId import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf +import kotlinx.parcelize.Parcelize /** * This sealed class represents all the permalink cases. * You don't have to instantiate yourself but should use [PermalinkParser] instead. */ @Immutable -sealed interface PermalinkData { +@Parcelize +sealed interface PermalinkData : Parcelable { data class RoomLink( val roomIdOrAlias: RoomIdOrAlias, val eventId: EventId? = null, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/recentemojis/AddRecentEmoji.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/recentemojis/AddRecentEmoji.kt new file mode 100644 index 0000000000..da657ea78a --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/recentemojis/AddRecentEmoji.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2025 New Vector 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.matrix.api.recentemojis + +import dev.zacsweers.metro.Inject +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.matrix.api.MatrixClient +import kotlinx.coroutines.withContext + +@Inject +class AddRecentEmoji( + private val client: MatrixClient, + private val dispatchers: CoroutineDispatchers, +) { + suspend operator fun invoke(emoji: String): Result = withContext(dispatchers.io) { + client.addRecentEmoji(emoji) + } +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/recentemojis/GetRecentEmojis.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/recentemojis/GetRecentEmojis.kt new file mode 100644 index 0000000000..53adf88c37 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/recentemojis/GetRecentEmojis.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2025 New Vector 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.matrix.api.recentemojis + +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.MatrixClient +import kotlinx.coroutines.withContext + +fun interface GetRecentEmojis { + suspend operator fun invoke(): Result> +} + +@ContributesBinding(SessionScope::class) +@Inject +class DefaultGetRecentEmojis( + private val client: MatrixClient, + private val dispatchers: CoroutineDispatchers, +) : GetRecentEmojis { + override suspend operator fun invoke(): Result> = withContext(dispatchers.io) { + client.getRecentEmojis() + } +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/BaseRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/BaseRoom.kt index 84aae82b66..2694191f89 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/BaseRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/BaseRoom.kt @@ -244,7 +244,9 @@ interface BaseRoom : Closeable { suspend fun subscribeToCallDecline(notificationEventId: EventId): Flow - /** + suspend fun threadRootIdForEvent(eventId: EventId): Result + + /** * Destroy the room and release all resources associated to it. */ fun destroy() diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MessageEventType.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MessageEventType.kt index 5d20840cf7..0f2a61da6e 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MessageEventType.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MessageEventType.kt @@ -7,28 +7,32 @@ package io.element.android.libraries.matrix.api.room -enum class MessageEventType { - CALL_ANSWER, - CALL_INVITE, - CALL_HANGUP, - CALL_CANDIDATES, - RTC_NOTIFICATION, - KEY_VERIFICATION_READY, - KEY_VERIFICATION_START, - KEY_VERIFICATION_CANCEL, - KEY_VERIFICATION_ACCEPT, - KEY_VERIFICATION_KEY, - KEY_VERIFICATION_MAC, - KEY_VERIFICATION_DONE, - REACTION, - ROOM_ENCRYPTED, - ROOM_MESSAGE, - ROOM_REDACTION, - STICKER, - POLL_END, - POLL_RESPONSE, - POLL_START, - UNSTABLE_POLL_END, - UNSTABLE_POLL_RESPONSE, - UNSTABLE_POLL_START, +import androidx.compose.runtime.Immutable + +@Immutable +sealed interface MessageEventType { + data object CallAnswer : MessageEventType + data object CallInvite : MessageEventType + data object CallHangup : MessageEventType + data object CallCandidates : MessageEventType + data object RtcNotification : MessageEventType + data object KeyVerificationReady : MessageEventType + data object KeyVerificationStart : MessageEventType + data object KeyVerificationCancel : MessageEventType + data object KeyVerificationAccept : MessageEventType + data object KeyVerificationKey : MessageEventType + data object KeyVerificationMac : MessageEventType + data object KeyVerificationDone : MessageEventType + data object Reaction : MessageEventType + data object RoomEncrypted : MessageEventType + data object RoomMessage : MessageEventType + data object RoomRedaction : MessageEventType + data object Sticker : MessageEventType + data object PollEnd : MessageEventType + data object PollResponse : MessageEventType + data object PollStart : MessageEventType + data object UnstablePollEnd : MessageEventType + data object UnstablePollResponse : MessageEventType + data object UnstablePollStart : MessageEventType + data class Other(val type: String) : MessageEventType } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMembershipObserver.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMembershipObserver.kt index 19d7fdaaf2..a4a718b219 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMembershipObserver.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMembershipObserver.kt @@ -15,6 +15,7 @@ import kotlinx.coroutines.flow.asSharedFlow class RoomMembershipObserver { data class RoomMembershipUpdate( val roomId: RoomId, + val isSpace: Boolean, val isUserInRoom: Boolean, val change: MembershipChange, ) @@ -22,12 +23,23 @@ class RoomMembershipObserver { private val _updates = MutableSharedFlow(extraBufferCapacity = 10) val updates = _updates.asSharedFlow() - suspend fun notifyUserLeftRoom(roomId: RoomId, membershipBeforeLeft: CurrentUserMembership) { + suspend fun notifyUserLeftRoom( + roomId: RoomId, + isSpace: Boolean, + membershipBeforeLeft: CurrentUserMembership, + ) { val membershipChange = when (membershipBeforeLeft) { CurrentUserMembership.INVITED -> MembershipChange.INVITATION_REJECTED CurrentUserMembership.KNOCKED -> MembershipChange.KNOCK_RETRACTED else -> MembershipChange.LEFT } - _updates.emit(RoomMembershipUpdate(roomId, false, membershipChange)) + _updates.emit( + RoomMembershipUpdate( + roomId = roomId, + isSpace = isSpace, + isUserInRoom = false, + change = membershipChange, + ) + ) } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomMembersWithRole.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomMembersWithRole.kt index 30719626a8..689157f215 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomMembersWithRole.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomMembersWithRole.kt @@ -12,7 +12,7 @@ import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembersState import io.element.android.libraries.matrix.api.room.activeRoomMembers import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged @@ -38,7 +38,7 @@ fun BaseRoom.usersWithRole(role: RoomMember.Role): Flow membersState.activeRoomMembers() .filter { powerLevels.contains(it.userId) } - .toPersistentList() + .toImmutableList() } .distinctUntilChanged() } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListFilter.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListFilter.kt index 8b9b84eb18..405906a185 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListFilter.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListFilter.kt @@ -58,11 +58,12 @@ sealed interface RoomListFilter { data object Invite : RoomListFilter /** - * A filter that matches either Group or People rooms. + * A filter that matches either Group,People rooms or Space. */ sealed interface Category : RoomListFilter { data object Group : Category data object People : Category + data object Space : Category } /** diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/LeaveSpaceHandle.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/LeaveSpaceHandle.kt new file mode 100644 index 0000000000..292a973dda --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/LeaveSpaceHandle.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2025 New Vector 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.matrix.api.spaces + +import io.element.android.libraries.matrix.api.core.RoomId + +interface LeaveSpaceHandle { + /** + * The id of the space to leave. + */ + val id: RoomId + + /** + * Get a list of rooms that can be left when leaving the space. + * It will include the current space and all the subspaces and rooms that the user has joined. + */ + suspend fun rooms(): Result> + + /** + * Leave the space and the given rooms. + * If [roomIds] is empty, only the space will be left. + */ + suspend fun leave(roomIds: List): Result + + /** + * Close the handle and free resources. + */ + fun close() +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/LeaveSpaceRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/LeaveSpaceRoom.kt new file mode 100644 index 0000000000..fb90896e05 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/LeaveSpaceRoom.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2025 New Vector 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.matrix.api.spaces + +data class LeaveSpaceRoom( + val spaceRoom: SpaceRoom, + val isLastAdmin: Boolean, +) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoom.kt index d4e1d57826..3e72632e6c 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoom.kt @@ -13,14 +13,16 @@ import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.RoomType import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.user.MatrixUser +import kotlinx.collections.immutable.ImmutableList data class SpaceRoom( - val name: String?, + val rawName: String?, + val displayName: String, val avatarUrl: String?, val canonicalAlias: RoomAlias?, val childrenCount: Int, val guestCanJoin: Boolean, - val heroes: List, + val heroes: ImmutableList, val joinRule: JoinRule?, val numJoinedMembers: Int, val roomId: RoomId, @@ -28,6 +30,13 @@ data class SpaceRoom( val state: CurrentUserMembership?, val topic: String?, val worldReadable: Boolean, + /** + * The via parameters of the room. + */ + val via: ImmutableList, + val isDirect: Boolean?, ) { val isSpace = roomType == RoomType.Space + + val visibility = SpaceRoomVisibility.fromJoinRule(joinRule) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoomList.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoomList.kt index e55e1b87bd..1dddfadc5b 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoomList.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoomList.kt @@ -7,6 +7,7 @@ package io.element.android.libraries.matrix.api.spaces +import io.element.android.libraries.matrix.api.core.RoomId import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import java.util.Optional @@ -17,9 +18,13 @@ interface SpaceRoomList { data class Idle(val hasMoreToLoad: Boolean) : PaginationStatus } - fun currentSpaceFlow(): StateFlow> + val roomId: RoomId + + val currentSpaceFlow: StateFlow> val spaceRoomsFlow: Flow> val paginationStatusFlow: StateFlow suspend fun paginate(): Result + + fun destroy() } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoomVisibility.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoomVisibility.kt new file mode 100644 index 0000000000..98afa1d508 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoomVisibility.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2025 New Vector 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.matrix.api.spaces + +import androidx.compose.runtime.Immutable +import io.element.android.libraries.matrix.api.room.join.JoinRule +@Immutable +sealed interface SpaceRoomVisibility { + data object Private : SpaceRoomVisibility + data object Public : SpaceRoomVisibility + data object Restricted : SpaceRoomVisibility + + companion object { + fun fromJoinRule(joinRule: JoinRule?): SpaceRoomVisibility = when (joinRule) { + JoinRule.Public -> Public + is JoinRule.Restricted, is JoinRule.KnockRestricted -> Restricted + // Else fallback to Private + else -> Private + } + } +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceService.kt index b4572ad0bb..f1fea6b62a 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceService.kt @@ -15,4 +15,6 @@ interface SpaceService { suspend fun joinedSpaces(): Result> fun spaceRoomList(id: RoomId): SpaceRoomList + + fun getLeaveSpaceHandle(spaceId: RoomId): LeaveSpaceHandle } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/user/CurrentSessionIdHolder.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/user/CurrentSessionIdHolder.kt deleted file mode 100644 index 171f8337e0..0000000000 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/user/CurrentSessionIdHolder.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2023, 2024 New Vector 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.matrix.api.user - -import dev.zacsweers.metro.Inject -import dev.zacsweers.metro.SingleIn -import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.api.MatrixClient - -@SingleIn(SessionScope::class) -@Inject -class CurrentSessionIdHolder(matrixClient: MatrixClient) { - val current = matrixClient.sessionId -} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationRequestDetails.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationRequestDetails.kt index 897e17c611..edf8c6ff78 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationRequestDetails.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationRequestDetails.kt @@ -10,20 +10,14 @@ package io.element.android.libraries.matrix.api.verification import android.os.Parcelable import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.FlowId -import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.parcelize.Parcelize @Parcelize data class SessionVerificationRequestDetails( - val senderProfile: SenderProfile, + val senderProfile: MatrixUser, val flowId: FlowId, val deviceId: DeviceId, + val deviceDisplayName: String?, val firstSeenTimestamp: Long, -) : Parcelable { - @Parcelize - data class SenderProfile( - val userId: UserId, - val displayName: String?, - val avatarUrl: String?, - ) : Parcelable -} +) : Parcelable diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts index 33ab700022..14ba9df71f 100644 --- a/libraries/matrix/impl/build.gradle.kts +++ b/libraries/matrix/impl/build.gradle.kts @@ -37,7 +37,7 @@ dependencies { implementation(projects.services.toolbox.api) api(projects.libraries.matrix.api) implementation(projects.libraries.core) - implementation("net.java.dev.jna:jna:5.17.0@aar") + implementation("net.java.dev.jna:jna:5.18.1@aar") implementation(libs.androidx.datastore.preferences) implementation(libs.serialization.json) implementation(libs.kotlinx.collections.immutable) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 956e645571..c646e2ee18 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -23,13 +23,8 @@ import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters import io.element.android.libraries.matrix.api.createroom.RoomPreset -import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.media.MatrixMediaLoader -import io.element.android.libraries.matrix.api.media.MediaPreviewService -import io.element.android.libraries.matrix.api.notification.NotificationService -import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.oidc.AccountManagementAction -import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.JoinedRoom @@ -39,18 +34,16 @@ import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias import io.element.android.libraries.matrix.api.room.join.JoinRule -import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.spaces.SpaceService import io.element.android.libraries.matrix.api.sync.SlidingSyncVersion -import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults import io.element.android.libraries.matrix.api.user.MatrixUser -import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.impl.encryption.RustEncryptionService import io.element.android.libraries.matrix.impl.exception.mapClientException +import io.element.android.libraries.matrix.impl.mapper.map import io.element.android.libraries.matrix.impl.media.RustMediaLoader import io.element.android.libraries.matrix.impl.media.RustMediaPreviewService import io.element.android.libraries.matrix.impl.notification.RustNotificationService @@ -75,7 +68,6 @@ import io.element.android.libraries.matrix.impl.roomlist.roomOrNull import io.element.android.libraries.matrix.impl.spaces.RustSpaceService import io.element.android.libraries.matrix.impl.sync.RustSyncService import io.element.android.libraries.matrix.impl.sync.map -import io.element.android.libraries.matrix.impl.usersearch.UserProfileMapper import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper import io.element.android.libraries.matrix.impl.util.SessionPathsProvider import io.element.android.libraries.matrix.impl.util.cancelAndDestroy @@ -85,7 +77,7 @@ import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.services.toolbox.api.systemclock.SystemClock import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.cancel @@ -128,11 +120,10 @@ import org.matrix.rustcomponents.sdk.SyncService as ClientSyncService class RustMatrixClient( private val innerClient: Client, - private val baseDirectory: File, private val sessionStore: SessionStore, - private val appCoroutineScope: CoroutineScope, private val sessionDelegate: RustClientSessionDelegate, private val innerSyncService: ClientSyncService, + appCoroutineScope: CoroutineScope, dispatchers: CoroutineDispatchers, baseCacheDirectory: File, clock: SystemClock, @@ -147,27 +138,29 @@ class RustMatrixClient( private val innerRoomListService = innerSyncService.roomListService() private val innerSpaceService = innerClient.spaceService() - private val rustSyncService = RustSyncService( + override val roomMembershipObserver = RoomMembershipObserver() + + override val syncService = RustSyncService( inner = innerSyncService, dispatcher = sessionDispatcher, sessionCoroutineScope = sessionCoroutineScope ) - private val pushersService = RustPushersService( + override val pushersService = RustPushersService( client = innerClient, dispatchers = dispatchers, ) private val notificationProcessSetup = NotificationProcessSetup.SingleProcess(innerSyncService) private val innerNotificationClient = runBlocking { innerClient.notificationClient(notificationProcessSetup) } - private val notificationService = RustNotificationService(sessionId, innerNotificationClient, dispatchers, clock) - private val notificationSettingsService = RustNotificationSettingsService(innerClient, sessionCoroutineScope, dispatchers) - private val encryptionService = RustEncryptionService( + override val notificationService = RustNotificationService(sessionId, innerNotificationClient, dispatchers, clock) + override val notificationSettingsService = RustNotificationSettingsService(innerClient, sessionCoroutineScope, dispatchers) + override val encryptionService = RustEncryptionService( client = innerClient, - syncService = rustSyncService, + syncService = syncService, sessionCoroutineScope = sessionCoroutineScope, dispatchers = dispatchers, ) - private val roomDirectoryService = RustRoomDirectoryService( + override val roomDirectoryService = RustRoomDirectoryService( client = innerClient, sessionDispatcher = sessionDispatcher, ) @@ -189,18 +182,19 @@ class RustMatrixClient( override val spaceService: SpaceService = RustSpaceService( innerSpaceService = innerSpaceService, + roomMembershipObserver = roomMembershipObserver, sessionCoroutineScope = sessionCoroutineScope, sessionDispatcher = sessionDispatcher, ) - private val verificationService = RustSessionVerificationService( + override val sessionVerificationService = RustSessionVerificationService( client = innerClient, - isSyncServiceReady = rustSyncService.syncState.map { it == SyncState.Running }, + isSyncServiceReady = syncService.syncState.map { it == SyncState.Running }, sessionCoroutineScope = sessionCoroutineScope, ) private val roomInfoMapper = RoomInfoMapper() - private val roomMembershipObserver = RoomMembershipObserver() + private val roomFactory = RustRoomFactory( roomListService = roomListService, innerRoomListService = innerRoomListService, @@ -218,13 +212,13 @@ class RustMatrixClient( featureFlagService = featureFlagService, ) - override val mediaLoader: MatrixMediaLoader = RustMediaLoader( + override val matrixMediaLoader: MatrixMediaLoader = RustMediaLoader( baseCacheDirectory = baseCacheDirectory, dispatchers = dispatchers, innerClient = innerClient, ) - private val mediaPreviewService = RustMediaPreviewService( + override val mediaPreviewService = RustMediaPreviewService( sessionCoroutineScope = sessionCoroutineScope, innerClient = innerClient, sessionDispatcher = sessionDispatcher, @@ -235,7 +229,6 @@ class RustMatrixClient( private val _userProfile: MutableStateFlow = MutableStateFlow( MatrixUser( userId = sessionId, - // TODO cache for displayName? displayName = null, avatarUrl = null, ) @@ -245,11 +238,11 @@ class RustMatrixClient( override val ignoredUsersFlow = mxCallbackFlow> { // Fetch the initial value manually, the SDK won't return it automatically - channel.trySend(innerClient.ignoredUsers().map(::UserId).toPersistentList()) + channel.trySend(innerClient.ignoredUsers().map(::UserId).toImmutableList()) innerClient.subscribeToIgnoredUsers(object : IgnoredUsersListener { override fun call(ignoredUserIds: List) { - channel.trySend(ignoredUserIds.map(::UserId).toPersistentList()) + channel.trySend(ignoredUserIds.map(::UserId).toImmutableList()) } }) } @@ -264,6 +257,16 @@ class RustMatrixClient( // Start notification settings notificationSettingsService.start() + // Update the user profile in the session store if needed + sessionStore.getSession(sessionId.value)?.let { sessionData -> + _userProfile.emit( + MatrixUser( + userId = sessionId, + displayName = sessionData.userDisplayName, + avatarUrl = sessionData.userAvatarUrl, + ) + ) + } // Force a refresh of the profile getUserProfile() } @@ -394,12 +397,20 @@ class RustMatrixClient( override suspend fun getProfile(userId: UserId): Result = withContext(sessionDispatcher) { runCatchingExceptions { - innerClient.getProfile(userId.value).let(UserProfileMapper::map) + innerClient.getProfile(userId.value).map() } } override suspend fun getUserProfile(): Result = getProfile(sessionId) - .onSuccess { _userProfile.tryEmit(it) } + .onSuccess { matrixUser -> + _userProfile.emit(matrixUser) + // Also update our session storage + sessionStore.updateUserProfile( + sessionId = sessionId.value, + displayName = matrixUser.displayName, + avatarUrl = matrixUser.avatarUrl, + ) + } override suspend fun searchUsers(searchTerm: String, limit: Long): Result = withContext(sessionDispatcher) { @@ -518,33 +529,17 @@ class RustMatrixClient( }.mapFailure { it.mapClientException() } } - override fun syncService(): SyncService = rustSyncService - - override fun sessionVerificationService(): SessionVerificationService = verificationService - - override fun pushersService(): PushersService = pushersService - - override fun notificationService(): NotificationService = notificationService - - override fun encryptionService(): EncryptionService = encryptionService - - override fun notificationSettingsService(): NotificationSettingsService = notificationSettingsService - - override fun roomDirectoryService(): RoomDirectoryService = roomDirectoryService - - override fun mediaPreviewService(): MediaPreviewService = mediaPreviewService - internal suspend fun destroy() { innerNotificationClient.close() roomFactory.destroy() - rustSyncService.destroy() + syncService.destroy() notificationSettingsService.destroy() notificationProcessSetup.destroy() sessionCoroutineScope.cancel() clientDelegateTaskHandle?.cancelAndDestroy() - verificationService.destroy() + sessionVerificationService.destroy() sessionDelegate.clearCurrentClient() innerRoomListService.close() @@ -555,7 +550,7 @@ class RustMatrixClient( } override suspend fun getCacheSize(): Long { - return baseDirectory.getCacheSize() + return getCacheSize(includeCryptoDb = false) } override suspend fun clearCache() { @@ -654,8 +649,6 @@ class RustMatrixClient( } } - override fun roomMembershipObserver(): RoomMembershipObserver = roomMembershipObserver - override fun getRoomInfoFlow(roomId: RoomId): Flow> { return mxCallbackFlow { val roomNotFound = innerRoomListService.roomOrNull(roomId.value).use { it == null } @@ -708,7 +701,19 @@ class RustMatrixClient( runCatchingExceptions { innerClient.getMaxMediaUploadSize().toLong() } } - private suspend fun File.getCacheSize( + override suspend fun addRecentEmoji(emoji: String): Result = withContext(sessionDispatcher) { + runCatchingExceptions { + innerClient.addRecentEmoji(emoji) + } + } + + override suspend fun getRecentEmojis(): Result> = withContext(sessionDispatcher) { + runCatchingExceptions { + innerClient.getRecentEmojis().map { it.emoji } + } + } + + private suspend fun getCacheSize( includeCryptoDb: Boolean = false, ): Long = withContext(sessionDispatcher) { val sessionDirectory = sessionPathsProvider.provides(sessionId) ?: return@withContext 0L 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 572bcbfd18..35b5bcf2b9 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 @@ -9,7 +9,6 @@ package io.element.android.libraries.matrix.impl import dev.zacsweers.metro.Inject import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.di.BaseDirectory import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.di.annotations.AppCoroutineScope import io.element.android.libraries.featureflag.api.FeatureFlagService @@ -43,7 +42,6 @@ import java.io.File @Inject class RustMatrixClientFactory( - @BaseDirectory private val baseDirectory: File, @CacheDirectory private val cacheDirectory: File, @AppCoroutineScope private val appCoroutineScope: CoroutineScope, @@ -87,7 +85,6 @@ class RustMatrixClientFactory( return RustMatrixClient( innerClient = client, - baseDirectory = baseDirectory, sessionStore = sessionStore, appCoroutineScope = appCoroutineScope, sessionDelegate = sessionDelegate, @@ -136,13 +133,15 @@ class RustMatrixClientFactory( ) .enableShareHistoryOnInvite(featureFlagService.isFeatureEnabled(FeatureFlags.EnableKeyShareOnInvite)) .threadsEnabled(featureFlagService.isFeatureEnabled(FeatureFlags.Threads), threadSubscriptions = false) - .requestConfig(RequestConfig( - timeout = 30_000uL, - retryLimit = 0u, - // Use default values for the rest - maxConcurrentRequests = null, - maxRetryTime = null, - )) + .requestConfig( + RequestConfig( + timeout = 30_000uL, + retryLimit = 0u, + // Use default values for the rest + maxConcurrentRequests = null, + maxRetryTime = null, + ) + ) .run { // Apply sliding sync version settings when (slidingSyncType) { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt index 7175913dad..05eb4c4d5f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt @@ -14,6 +14,7 @@ import org.matrix.rustcomponents.sdk.OidcException fun Throwable.mapAuthenticationException(): AuthenticationException { val message = this.message ?: "Unknown error" return when (this) { + is AuthenticationException -> this is ClientBuildException -> when (this) { is ClientBuildException.Generic -> AuthenticationException.Generic(message) is ClientBuildException.InvalidServerName -> AuthenticationException.InvalidServerName(message) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt index 24201aaf63..88c86a43d6 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt @@ -15,6 +15,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.auth.AuthenticationException import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.api.auth.OidcDetails @@ -139,6 +140,8 @@ class RustMatrixAuthenticationService( val client = currentClient ?: error("You need to call `setHomeserver()` first") val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first") client.login(username, password, "Element X Android", null) + // Ensure that the user is not already logged in with the same account + ensureNotAlreadyLoggedIn(client) val sessionData = client.session() .toSessionData( isTokenValid = true, @@ -227,17 +230,19 @@ class RustMatrixAuthenticationService( val client = currentClient ?: error("You need to call `setHomeserver()` first") val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first") client.loginWithOidcCallback(callbackUrl) + + // Free the pending data since we won't use it to abort the flow anymore + pendingOAuthAuthorizationData?.close() + pendingOAuthAuthorizationData = null + + // Ensure that the user is not already logged in with the same account + ensureNotAlreadyLoggedIn(client) val sessionData = client.session().toSessionData( isTokenValid = true, loginType = LoginType.OIDC, passphrase = pendingPassphrase, sessionPaths = currentSessionPaths, ) - - // Free the pending data since we won't use it to abort the flow anymore - pendingOAuthAuthorizationData?.close() - pendingOAuthAuthorizationData = null - val matrixClient = rustMatrixClientFactory.create(client) newMatrixClientObservers.forEach { it.invoke(matrixClient) } sessionStore.addSession(sessionData) @@ -253,6 +258,21 @@ class RustMatrixAuthenticationService( } } + @Throws(AuthenticationException.AccountAlreadyLoggedIn::class) + private suspend fun ensureNotAlreadyLoggedIn(client: Client) { + val newUserId = client.userId() + val accountAlreadyLoggedIn = sessionStore.getAllSessions().any { + it.userId == newUserId + } + if (accountAlreadyLoggedIn) { + // Sign out the client, ignoring any error + runCatchingExceptions { + client.logout() + } + throw AuthenticationException.AccountAlreadyLoggedIn(newUserId) + } + } + override suspend fun loginWithQrCode(qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit) = withContext(coroutineDispatchers.io) { val sdkQrCodeLoginData = (qrCodeData as SdkQrCodeLoginData).rustQrCodeData @@ -275,7 +295,8 @@ class RustMatrixAuthenticationService( oidcConfiguration = oidcConfiguration, progressListener = progressListener, ) - + // Ensure that the user is not already logged in with the same account + ensureNotAlreadyLoggedIn(client) val sessionData = client.session() .toSessionData( isTokenValid = true, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt index ef24cbba1a..37cf406f5f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt @@ -13,6 +13,7 @@ import dev.zacsweers.metro.Provides import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.media.MediaPreviewService @@ -20,6 +21,7 @@ import io.element.android.libraries.matrix.api.notificationsettings.Notification import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService import io.element.android.libraries.matrix.api.roomlist.RoomListService +import io.element.android.libraries.matrix.api.spaces.SpaceService import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.verification.SessionVerificationService import kotlinx.coroutines.CoroutineScope @@ -27,19 +29,24 @@ import kotlinx.coroutines.CoroutineScope @BindingContainer @ContributesTo(SessionScope::class) object SessionMatrixModule { + @Provides + fun providesSessionId(matrixClient: MatrixClient): SessionId { + return matrixClient.sessionId + } + @Provides fun providesSessionVerificationService(matrixClient: MatrixClient): SessionVerificationService { - return matrixClient.sessionVerificationService() + return matrixClient.sessionVerificationService } @Provides fun providesNotificationSettingsService(matrixClient: MatrixClient): NotificationSettingsService { - return matrixClient.notificationSettingsService() + return matrixClient.notificationSettingsService } @Provides fun provideRoomMembershipObserver(matrixClient: MatrixClient): RoomMembershipObserver { - return matrixClient.roomMembershipObserver() + return matrixClient.roomMembershipObserver } @Provides @@ -49,32 +56,37 @@ object SessionMatrixModule { @Provides fun providesSyncService(matrixClient: MatrixClient): SyncService { - return matrixClient.syncService() + return matrixClient.syncService } @Provides fun providesEncryptionService(matrixClient: MatrixClient): EncryptionService { - return matrixClient.encryptionService() + return matrixClient.encryptionService } @Provides - fun provideMediaLoader(matrixClient: MatrixClient): MatrixMediaLoader { - return matrixClient.mediaLoader + fun providesMatrixMediaLoader(matrixClient: MatrixClient): MatrixMediaLoader { + return matrixClient.matrixMediaLoader } @SessionCoroutineScope @Provides - fun provideSessionCoroutineScope(matrixClient: MatrixClient): CoroutineScope { + fun providesSessionCoroutineScope(matrixClient: MatrixClient): CoroutineScope { return matrixClient.sessionCoroutineScope } @Provides fun providesRoomDirectoryService(matrixClient: MatrixClient): RoomDirectoryService { - return matrixClient.roomDirectoryService() + return matrixClient.roomDirectoryService } @Provides fun providesMediaPreviewService(matrixClient: MatrixClient): MediaPreviewService { - return matrixClient.mediaPreviewService() + return matrixClient.mediaPreviewService + } + + @Provides + fun providesSpaceService(matrixClient: MatrixClient): SpaceService { + return matrixClient.spaceService } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RecoveryExceptionMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RecoveryExceptionMapper.kt index b9679eb160..5568d008ec 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RecoveryExceptionMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RecoveryExceptionMapper.kt @@ -20,6 +20,9 @@ fun Throwable.mapRecoveryException(): RecoveryException { message = errorMessage ) is RustRecoveryException.BackupExistsOnServer -> RecoveryException.BackupExistsOnServer + is RustRecoveryException.Import -> RecoveryException.Import( + message = errorMessage + ) is RustRecoveryException.Client -> RecoveryException.Client( source.mapClientException() ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt index 7c87666fe7..91cafd1df0 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt @@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.encryption.IdentityResetHandle import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.sync.SyncState +import io.element.android.libraries.matrix.impl.exception.mapClientException import io.element.android.libraries.matrix.impl.sync.RustSyncService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose @@ -43,9 +44,10 @@ import org.matrix.rustcomponents.sdk.Encryption import org.matrix.rustcomponents.sdk.UserIdentity import org.matrix.rustcomponents.sdk.BackupUploadState as RustBackupUploadState import org.matrix.rustcomponents.sdk.EnableRecoveryProgress as RustEnableRecoveryProgress +import org.matrix.rustcomponents.sdk.RecoveryException as RustRecoveryException import org.matrix.rustcomponents.sdk.SteadyStateException as RustSteadyStateException -internal class RustEncryptionService( +class RustEncryptionService( client: Client, syncService: RustSyncService, sessionCoroutineScope: CoroutineScope, @@ -96,6 +98,20 @@ internal class RustEncryptionService( } .stateIn(sessionCoroutineScope, SharingStarted.Eagerly, false) + /** + * Check if the user has any devices available to verify against every 5 seconds. + * TODO This is a temporary workaround, when we will have a way to observe + * the sessions, this code will have to be updated. + */ + override val hasDevicesToVerifyAgainst: StateFlow = flow { + while (currentCoroutineContext().isActive) { + val result = hasDevicesToVerifyAgainst().getOrDefault(false) + emit(result) + delay(5_000) + } + } + .stateIn(sessionCoroutineScope, SharingStarted.Eagerly, false) + override suspend fun enableBackups(): Result = withContext(dispatchers.io) { runCatchingExceptions { service.enableBackups() @@ -171,6 +187,14 @@ internal class RustEncryptionService( } } + private suspend fun hasDevicesToVerifyAgainst(): Result = withContext(dispatchers.io) { + runCatchingExceptions { + service.hasDevicesToVerifyAgainst() + }.mapFailure { + it.mapClientException() + } + } + override suspend fun resetRecoveryKey(): Result = withContext(dispatchers.io) { runCatchingExceptions { service.resetRecoveryKey() @@ -182,8 +206,12 @@ internal class RustEncryptionService( override suspend fun recover(recoveryKey: String): Result = withContext(dispatchers.io) { runCatchingExceptions { service.recover(recoveryKey) - }.mapFailure { - it.mapRecoveryException() + }.recoverCatching { + when (it) { + // We ignore import errors because the user will be notified about them via the "Key storage out of sync" detection. + is RustRecoveryException.Import -> Unit + else -> throw it.mapRecoveryException() + } } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt index 1d45c47470..2b5cac67ea 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt @@ -34,6 +34,11 @@ internal fun Session.toSessionData( passphrase = passphrase, sessionPath = sessionPaths.fileDirectory.absolutePath, cachePath = sessionPaths.cacheDirectory.absolutePath, + // Note: position and lastUsageIndex will be set by the SessionStore when adding the session + position = 0, + lastUsageIndex = 0, + userDisplayName = null, + userAvatarUrl = null, ) internal fun ExternalSession.toSessionData( @@ -55,4 +60,8 @@ internal fun ExternalSession.toSessionData( passphrase = passphrase, sessionPath = sessionPaths.fileDirectory.absolutePath, cachePath = sessionPaths.cacheDirectory.absolutePath, + position = 0, + lastUsageIndex = 0, + userDisplayName = null, + userAvatarUrl = null, ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserProfileMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/UserProfileMapper.kt similarity index 53% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserProfileMapper.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/UserProfileMapper.kt index f8f842c4be..6cc338610b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserProfileMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/UserProfileMapper.kt @@ -5,17 +5,14 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.impl.usersearch +package io.element.android.libraries.matrix.impl.mapper import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser import org.matrix.rustcomponents.sdk.UserProfile -object UserProfileMapper { - fun map(userProfile: UserProfile): MatrixUser = - MatrixUser( - userId = UserId(userProfile.userId), - displayName = userProfile.displayName, - avatarUrl = userProfile.avatarUrl, - ) -} +fun UserProfile.map() = MatrixUser( + userId = UserId(userId), + displayName = displayName, + avatarUrl = avatarUrl, +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MessageEventType.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MessageEventType.kt index 4e88f0971e..14bd6d5e9d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MessageEventType.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MessageEventType.kt @@ -11,53 +11,55 @@ import io.element.android.libraries.matrix.api.room.MessageEventType import org.matrix.rustcomponents.sdk.MessageLikeEventType fun MessageEventType.map(): MessageLikeEventType = when (this) { - MessageEventType.CALL_ANSWER -> MessageLikeEventType.CALL_ANSWER - MessageEventType.CALL_INVITE -> MessageLikeEventType.CALL_INVITE - MessageEventType.CALL_HANGUP -> MessageLikeEventType.CALL_HANGUP - MessageEventType.CALL_CANDIDATES -> MessageLikeEventType.CALL_CANDIDATES - MessageEventType.RTC_NOTIFICATION -> MessageLikeEventType.RTC_NOTIFICATION - MessageEventType.KEY_VERIFICATION_READY -> MessageLikeEventType.KEY_VERIFICATION_READY - MessageEventType.KEY_VERIFICATION_START -> MessageLikeEventType.KEY_VERIFICATION_START - MessageEventType.KEY_VERIFICATION_CANCEL -> MessageLikeEventType.KEY_VERIFICATION_CANCEL - MessageEventType.KEY_VERIFICATION_ACCEPT -> MessageLikeEventType.KEY_VERIFICATION_ACCEPT - MessageEventType.KEY_VERIFICATION_KEY -> MessageLikeEventType.KEY_VERIFICATION_KEY - MessageEventType.KEY_VERIFICATION_MAC -> MessageLikeEventType.KEY_VERIFICATION_MAC - MessageEventType.KEY_VERIFICATION_DONE -> MessageLikeEventType.KEY_VERIFICATION_DONE - MessageEventType.REACTION -> MessageLikeEventType.REACTION - MessageEventType.ROOM_ENCRYPTED -> MessageLikeEventType.ROOM_ENCRYPTED - MessageEventType.ROOM_MESSAGE -> MessageLikeEventType.ROOM_MESSAGE - MessageEventType.ROOM_REDACTION -> MessageLikeEventType.ROOM_REDACTION - MessageEventType.STICKER -> MessageLikeEventType.STICKER - MessageEventType.POLL_END -> MessageLikeEventType.POLL_END - MessageEventType.POLL_RESPONSE -> MessageLikeEventType.POLL_RESPONSE - MessageEventType.POLL_START -> MessageLikeEventType.POLL_START - MessageEventType.UNSTABLE_POLL_END -> MessageLikeEventType.UNSTABLE_POLL_END - MessageEventType.UNSTABLE_POLL_RESPONSE -> MessageLikeEventType.UNSTABLE_POLL_RESPONSE - MessageEventType.UNSTABLE_POLL_START -> MessageLikeEventType.UNSTABLE_POLL_START + MessageEventType.CallAnswer -> MessageLikeEventType.CallAnswer + MessageEventType.CallInvite -> MessageLikeEventType.CallInvite + MessageEventType.CallHangup -> MessageLikeEventType.CallHangup + MessageEventType.CallCandidates -> MessageLikeEventType.CallCandidates + MessageEventType.RtcNotification -> MessageLikeEventType.RtcNotification + MessageEventType.KeyVerificationReady -> MessageLikeEventType.KeyVerificationReady + MessageEventType.KeyVerificationStart -> MessageLikeEventType.KeyVerificationStart + MessageEventType.KeyVerificationCancel -> MessageLikeEventType.KeyVerificationCancel + MessageEventType.KeyVerificationAccept -> MessageLikeEventType.KeyVerificationAccept + MessageEventType.KeyVerificationKey -> MessageLikeEventType.KeyVerificationKey + MessageEventType.KeyVerificationMac -> MessageLikeEventType.KeyVerificationMac + MessageEventType.KeyVerificationDone -> MessageLikeEventType.KeyVerificationDone + MessageEventType.Reaction -> MessageLikeEventType.Reaction + MessageEventType.RoomEncrypted -> MessageLikeEventType.RoomEncrypted + MessageEventType.RoomMessage -> MessageLikeEventType.RoomMessage + MessageEventType.RoomRedaction -> MessageLikeEventType.RoomRedaction + MessageEventType.Sticker -> MessageLikeEventType.Sticker + MessageEventType.PollEnd -> MessageLikeEventType.PollEnd + MessageEventType.PollResponse -> MessageLikeEventType.PollResponse + MessageEventType.PollStart -> MessageLikeEventType.PollStart + MessageEventType.UnstablePollEnd -> MessageLikeEventType.UnstablePollEnd + MessageEventType.UnstablePollResponse -> MessageLikeEventType.UnstablePollResponse + MessageEventType.UnstablePollStart -> MessageLikeEventType.UnstablePollStart + is MessageEventType.Other -> MessageLikeEventType.Other(type) } fun MessageLikeEventType.map(): MessageEventType = when (this) { - MessageLikeEventType.CALL_ANSWER -> MessageEventType.CALL_ANSWER - MessageLikeEventType.CALL_INVITE -> MessageEventType.CALL_INVITE - MessageLikeEventType.CALL_HANGUP -> MessageEventType.CALL_HANGUP - MessageLikeEventType.CALL_CANDIDATES -> MessageEventType.CALL_CANDIDATES - MessageLikeEventType.RTC_NOTIFICATION -> MessageEventType.RTC_NOTIFICATION - MessageLikeEventType.KEY_VERIFICATION_READY -> MessageEventType.KEY_VERIFICATION_READY - MessageLikeEventType.KEY_VERIFICATION_START -> MessageEventType.KEY_VERIFICATION_START - MessageLikeEventType.KEY_VERIFICATION_CANCEL -> MessageEventType.KEY_VERIFICATION_CANCEL - MessageLikeEventType.KEY_VERIFICATION_ACCEPT -> MessageEventType.KEY_VERIFICATION_ACCEPT - MessageLikeEventType.KEY_VERIFICATION_KEY -> MessageEventType.KEY_VERIFICATION_KEY - MessageLikeEventType.KEY_VERIFICATION_MAC -> MessageEventType.KEY_VERIFICATION_MAC - MessageLikeEventType.KEY_VERIFICATION_DONE -> MessageEventType.KEY_VERIFICATION_DONE - MessageLikeEventType.REACTION -> MessageEventType.REACTION - MessageLikeEventType.ROOM_ENCRYPTED -> MessageEventType.ROOM_ENCRYPTED - MessageLikeEventType.ROOM_MESSAGE -> MessageEventType.ROOM_MESSAGE - MessageLikeEventType.ROOM_REDACTION -> MessageEventType.ROOM_REDACTION - MessageLikeEventType.STICKER -> MessageEventType.STICKER - MessageLikeEventType.POLL_END -> MessageEventType.POLL_END - MessageLikeEventType.POLL_RESPONSE -> MessageEventType.POLL_RESPONSE - MessageLikeEventType.POLL_START -> MessageEventType.POLL_START - MessageLikeEventType.UNSTABLE_POLL_END -> MessageEventType.UNSTABLE_POLL_END - MessageLikeEventType.UNSTABLE_POLL_RESPONSE -> MessageEventType.UNSTABLE_POLL_RESPONSE - MessageLikeEventType.UNSTABLE_POLL_START -> MessageEventType.UNSTABLE_POLL_START + MessageLikeEventType.CallAnswer -> MessageEventType.CallAnswer + MessageLikeEventType.CallInvite -> MessageEventType.CallInvite + MessageLikeEventType.CallHangup -> MessageEventType.CallHangup + MessageLikeEventType.CallCandidates -> MessageEventType.CallCandidates + MessageLikeEventType.RtcNotification -> MessageEventType.RtcNotification + MessageLikeEventType.KeyVerificationReady -> MessageEventType.KeyVerificationReady + MessageLikeEventType.KeyVerificationStart -> MessageEventType.KeyVerificationStart + MessageLikeEventType.KeyVerificationCancel -> MessageEventType.KeyVerificationCancel + MessageLikeEventType.KeyVerificationAccept -> MessageEventType.KeyVerificationAccept + MessageLikeEventType.KeyVerificationKey -> MessageEventType.KeyVerificationKey + MessageLikeEventType.KeyVerificationMac -> MessageEventType.KeyVerificationMac + MessageLikeEventType.KeyVerificationDone -> MessageEventType.KeyVerificationDone + MessageLikeEventType.Reaction -> MessageEventType.Reaction + MessageLikeEventType.RoomEncrypted -> MessageEventType.RoomEncrypted + MessageLikeEventType.RoomMessage -> MessageEventType.RoomMessage + MessageLikeEventType.RoomRedaction -> MessageEventType.RoomRedaction + MessageLikeEventType.Sticker -> MessageEventType.Sticker + MessageLikeEventType.PollEnd -> MessageEventType.PollEnd + MessageLikeEventType.PollResponse -> MessageEventType.PollResponse + MessageLikeEventType.PollStart -> MessageEventType.PollStart + MessageLikeEventType.UnstablePollEnd -> MessageEventType.UnstablePollEnd + MessageLikeEventType.UnstablePollResponse -> MessageEventType.UnstablePollResponse + MessageLikeEventType.UnstablePollStart -> MessageEventType.UnstablePollStart + is MessageLikeEventType.Other -> MessageEventType.Other(v1) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt index 199abcad44..4339b69101 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt @@ -22,7 +22,7 @@ import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper import io.element.android.libraries.matrix.impl.room.powerlevels.RoomPowerLevelsValuesMapper import io.element.android.libraries.matrix.impl.room.tombstone.map import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toPersistentMap +import kotlinx.collections.immutable.toImmutableMap import org.matrix.rustcomponents.sdk.Membership import org.matrix.rustcomponents.sdk.RoomHero import uniffi.matrix_sdk_base.EncryptionState @@ -103,6 +103,6 @@ fun RoomHero.map(): MatrixUser = MatrixUser( fun mapPowerLevels(roomPowerLevels: RustRoomPowerLevels): RoomPowerLevels { return RoomPowerLevels( values = RoomPowerLevelsValuesMapper.map(roomPowerLevels.values()), - users = roomPowerLevels.userPowerLevels().mapKeys { (key, _) -> UserId(key) }.toPersistentMap() + users = roomPowerLevels.userPowerLevels().mapKeys { (key, _) -> UserId(key) }.toImmutableMap() ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt index 1ca5915c71..6061ebd79c 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt @@ -157,7 +157,11 @@ class RustBaseRoom( runCatchingExceptions { innerRoom.leave() }.onSuccess { - roomMembershipObserver.notifyUserLeftRoom(roomId, membershipBeforeLeft) + roomMembershipObserver.notifyUserLeftRoom( + roomId = roomId, + isSpace = roomInfoFlow.value.isSpace, + membershipBeforeLeft = membershipBeforeLeft, + ) } } @@ -318,4 +322,12 @@ class RustBaseRoom( }) } } + + override suspend fun threadRootIdForEvent(eventId: EventId): Result = withContext(roomDispatcher) { + runCatchingExceptions { + innerRoom.loadOrFetchEvent(eventId.value).use { + it.threadRootEventId()?.let(::ThreadId) + } + } + } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/join/JoinRule.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/join/JoinRule.kt index 51422787e2..e145cbdb91 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/join/JoinRule.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/join/JoinRule.kt @@ -8,7 +8,7 @@ package io.element.android.libraries.matrix.impl.room.join import io.element.android.libraries.matrix.api.room.join.JoinRule -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import org.matrix.rustcomponents.sdk.JoinRule as RustJoinRule fun RustJoinRule.map(): JoinRule { @@ -17,9 +17,9 @@ fun RustJoinRule.map(): JoinRule { RustJoinRule.Private -> JoinRule.Private RustJoinRule.Knock -> JoinRule.Knock RustJoinRule.Invite -> JoinRule.Invite - is RustJoinRule.Restricted -> JoinRule.Restricted(rules.map { it.map() }.toPersistentList()) + is RustJoinRule.Restricted -> JoinRule.Restricted(rules.map { it.map() }.toImmutableList()) is RustJoinRule.Custom -> JoinRule.Custom(repr) - is RustJoinRule.KnockRestricted -> JoinRule.KnockRestricted(rules.map { it.map() }.toPersistentList()) + is RustJoinRule.KnockRestricted -> JoinRule.KnockRestricted(rules.map { it.map() }.toImmutableList()) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt index 15ba644fcf..53b8127ab8 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt @@ -29,7 +29,6 @@ import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList private val ROOM_LIST_RUST_FILTERS = listOf( RoomListEntriesDynamicFilterKind.NonLeft, - RoomListEntriesDynamicFilterKind.NonSpace, RoomListEntriesDynamicFilterKind.DeduplicateVersions ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilter.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilter.kt index 1e4a3fad3e..3fc59d7c44 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilter.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilter.kt @@ -15,41 +15,57 @@ import io.element.android.libraries.matrix.api.roomlist.RoomSummary val RoomListFilter.predicate get() = when (this) { - is RoomListFilter.All -> { _: RoomSummary -> true } - is RoomListFilter.Any -> { _: RoomSummary -> true } - RoomListFilter.None -> { _: RoomSummary -> false } + is RoomListFilter.All -> { roomSummary -> NonSpacePredicate(roomSummary) || IsInvitedPredicate(roomSummary) } + is RoomListFilter.Any -> { roomSummary -> NonSpacePredicate(roomSummary) || IsInvitedPredicate(roomSummary) } + RoomListFilter.None -> { _ -> false } RoomListFilter.Category.Group -> { roomSummary: RoomSummary -> - !roomSummary.info.isDm && !roomSummary.isInvited() + !roomSummary.info.isDm && NonInvitedPredicate(roomSummary) && NonSpacePredicate(roomSummary) } RoomListFilter.Category.People -> { roomSummary: RoomSummary -> - roomSummary.info.isDm && !roomSummary.isInvited() + roomSummary.info.isDm && NonInvitedPredicate(roomSummary) && NonSpacePredicate(roomSummary) } + RoomListFilter.Category.Space -> IsSpacePredicate RoomListFilter.Favorite -> { roomSummary: RoomSummary -> - roomSummary.info.isFavorite && !roomSummary.isInvited() + roomSummary.info.isFavorite && NonInvitedPredicate(roomSummary) && NonSpacePredicate(roomSummary) } RoomListFilter.Unread -> { roomSummary: RoomSummary -> - !roomSummary.isInvited() && (roomSummary.info.numUnreadNotifications > 0 || roomSummary.info.isMarkedUnread) + NonInvitedPredicate(roomSummary) && + NonSpacePredicate(roomSummary) && + (roomSummary.info.numUnreadNotifications > 0 || roomSummary.info.isMarkedUnread) } is RoomListFilter.NormalizedMatchRoomName -> { roomSummary: RoomSummary -> - roomSummary.info.name?.withoutAccents().orEmpty().contains(normalizedPattern, ignoreCase = true) - } - RoomListFilter.Invite -> { roomSummary: RoomSummary -> - roomSummary.isInvited() + roomSummary.info.name?.withoutAccents().orEmpty().contains(normalizedPattern, ignoreCase = true) && + (NonSpacePredicate(roomSummary) || IsInvitedPredicate(roomSummary)) } + RoomListFilter.Invite -> IsInvitedPredicate } fun List.filter(filter: RoomListFilter): List { return when (filter) { is RoomListFilter.All -> { - val predicates = filter.filters.map { it.predicate } + val predicates = if (filter.filters.isNotEmpty()) { + filter.filters.map { it.predicate } + } else { + listOf(filter.predicate) + } filter { roomSummary -> predicates.all { it(roomSummary) } } } is RoomListFilter.Any -> { - val predicates = filter.filters.map { it.predicate } + val predicates = if (filter.filters.isNotEmpty()) { + filter.filters.map { it.predicate } + } else { + listOf(filter.predicate) + } filter { roomSummary -> predicates.any { it(roomSummary) } } } else -> filter(filter.predicate) } } -private fun RoomSummary.isInvited() = info.currentUserMembership == CurrentUserMembership.INVITED +private val IsSpacePredicate = { roomSummary: RoomSummary -> roomSummary.info.isSpace } + +private val NonSpacePredicate = { roomSummary: RoomSummary -> !IsSpacePredicate(roomSummary) } + +private val IsInvitedPredicate = { roomSummary: RoomSummary -> roomSummary.info.currentUserMembership == CurrentUserMembership.INVITED } + +private val NonInvitedPredicate = { roomSummary: RoomSummary -> !IsInvitedPredicate(roomSummary) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustLeaveSpaceHandle.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustLeaveSpaceHandle.kt new file mode 100644 index 0000000000..e7e629b40b --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustLeaveSpaceHandle.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2025 New Vector 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.matrix.impl.spaces + +import io.element.android.libraries.core.extensions.runCatchingExceptions +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.CurrentUserMembership +import io.element.android.libraries.matrix.api.room.RoomMembershipObserver +import io.element.android.libraries.matrix.api.spaces.LeaveSpaceHandle +import io.element.android.libraries.matrix.api.spaces.LeaveSpaceRoom +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.launch +import timber.log.Timber +import org.matrix.rustcomponents.sdk.LeaveSpaceHandle as RustLeaveSpaceHandle + +class RustLeaveSpaceHandle( + override val id: RoomId, + private val spaceRoomMapper: SpaceRoomMapper, + private val roomMembershipObserver: RoomMembershipObserver, + sessionCoroutineScope: CoroutineScope, + private val innerProvider: suspend () -> RustLeaveSpaceHandle, +) : LeaveSpaceHandle { + private val inner = CompletableDeferred() + + init { + sessionCoroutineScope.launch { + inner.complete(innerProvider()) + } + } + + override suspend fun rooms(): Result> = runCatchingExceptions { + inner.await().rooms().map { leaveSpaceRoom -> + LeaveSpaceRoom( + spaceRoom = spaceRoomMapper.map(leaveSpaceRoom.spaceRoom), + isLastAdmin = leaveSpaceRoom.isLastAdmin, + ) + } + } + + override suspend fun leave(roomIds: List): Result = runCatchingExceptions { + // Ensure the space is included and is the last room to be left + val roomToLeave = roomIds - id + id + inner.await().leave(roomToLeave.map { it.value }) + }.onSuccess { + roomMembershipObserver.notifyUserLeftRoom( + roomId = id, + isSpace = true, + membershipBeforeLeft = CurrentUserMembership.JOINED, + ) + } + + @OptIn(ExperimentalCoroutinesApi::class) + override fun close() { + Timber.d("Destroying LeaveSpaceHandle $id") + try { + inner.getCompleted().destroy() + } catch (_: Exception) { + // Ignore, we just want to make sure it's completed + } + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceRoomList.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceRoomList.kt index 1a940bd2f0..b94c3ffd1b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceRoomList.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceRoomList.kt @@ -13,62 +13,78 @@ import io.element.android.libraries.matrix.api.spaces.SpaceRoom import io.element.android.libraries.matrix.api.spaces.SpaceRoomList import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import timber.log.Timber import uniffi.matrix_sdk_ui.SpaceRoomListPaginationState import java.util.Optional import org.matrix.rustcomponents.sdk.SpaceRoomList as InnerSpaceRoomList class RustSpaceRoomList( - private val roomId: RoomId, + override val roomId: RoomId, private val innerProvider: suspend () -> InnerSpaceRoomList, - sessionCoroutineScope: CoroutineScope, + private val coroutineScope: CoroutineScope, spaceRoomMapper: SpaceRoomMapper, - private val spaceRoomCache: SpaceRoomCache, ) : SpaceRoomList { - private val inner = CompletableDeferred() + private val innerCompletable = CompletableDeferred() - override fun currentSpaceFlow(): StateFlow> { - return spaceRoomCache.getSpaceRoomFlow(roomId) - } + override val currentSpaceFlow = MutableStateFlow>(Optional.empty()) override val spaceRoomsFlow = MutableSharedFlow>(replay = 1, extraBufferCapacity = Int.MAX_VALUE) + override val paginationStatusFlow: MutableStateFlow = MutableStateFlow(SpaceRoomList.PaginationStatus.Idle(hasMoreToLoad = false)) private val spaceListUpdateProcessor = SpaceListUpdateProcessor( spaceRoomsFlow = spaceRoomsFlow, - mapper = spaceRoomMapper, - spaceRoomCache = spaceRoomCache + mapper = spaceRoomMapper ) init { - sessionCoroutineScope.launch { - inner.complete(innerProvider()) - } - sessionCoroutineScope.launch { - inner.await().paginationStateFlow() + coroutineScope.launch { + val inner = innerProvider() + innerCompletable.complete(inner) + + inner.paginationStateFlow() .onEach { paginationStatus -> paginationStatusFlow.emit(paginationStatus.into()) } - .collect() - } + .launchIn(this) - sessionCoroutineScope.launch { - inner.await().spaceListUpdateFlow() + inner.spaceListUpdateFlow() .onEach { updates -> spaceListUpdateProcessor.postUpdates(updates) } - .collect() + .launchIn(this) + + inner.spaceUpdateFlow() + .map { space -> space.map(spaceRoomMapper::map) } + .onEach { space -> + currentSpaceFlow.emit(space) + } + .launchIn(this) } } override suspend fun paginate(): Result { return runCatchingExceptions { - inner.await().paginate() + innerCompletable.await().paginate() + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + override fun destroy() { + Timber.d("Destroying SpaceRoomList $roomId") + coroutineScope.cancel() + try { + innerCompletable.getCompleted().destroy() + } catch (_: Exception) { + // Ignore, we just want to make sure it's completed } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceService.kt index 2a63367577..88f738f0c4 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceService.kt @@ -7,8 +7,11 @@ package io.element.android.libraries.matrix.impl.spaces +import io.element.android.libraries.core.coroutine.childScope import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.RoomMembershipObserver +import io.element.android.libraries.matrix.api.spaces.LeaveSpaceHandle import io.element.android.libraries.matrix.api.spaces.SpaceRoom import io.element.android.libraries.matrix.api.spaces.SpaceRoomList import io.element.android.libraries.matrix.api.spaces.SpaceService @@ -36,14 +39,13 @@ class RustSpaceService( private val innerSpaceService: ClientSpaceService, private val sessionCoroutineScope: CoroutineScope, private val sessionDispatcher: CoroutineDispatcher, + private val roomMembershipObserver: RoomMembershipObserver, ) : SpaceService { private val spaceRoomMapper = SpaceRoomMapper() - private val spaceRoomCache = SpaceRoomCache() override val spaceRoomsFlow = MutableSharedFlow>(replay = 1, extraBufferCapacity = 1) private val spaceListUpdateProcessor = SpaceListUpdateProcessor( spaceRoomsFlow = spaceRoomsFlow, - mapper = spaceRoomMapper, - spaceRoomCache = spaceRoomCache + mapper = spaceRoomMapper ) override suspend fun joinedSpaces(): Result> = withContext(sessionDispatcher) { @@ -56,15 +58,26 @@ class RustSpaceService( } override fun spaceRoomList(id: RoomId): SpaceRoomList { + val childCoroutineScope = sessionCoroutineScope.childScope(sessionDispatcher, "SpaceRoomListScope-$this") return RustSpaceRoomList( roomId = id, innerProvider = { innerSpaceService.spaceRoomList(id.value) }, - sessionCoroutineScope = sessionCoroutineScope, + coroutineScope = childCoroutineScope, spaceRoomMapper = spaceRoomMapper, - spaceRoomCache = spaceRoomCache, ) } + override fun getLeaveSpaceHandle(spaceId: RoomId): LeaveSpaceHandle { + return RustLeaveSpaceHandle( + id = spaceId, + spaceRoomMapper = spaceRoomMapper, + roomMembershipObserver = roomMembershipObserver, + sessionCoroutineScope = sessionCoroutineScope, + ) { + innerSpaceService.leaveSpace(spaceId.value) + } + } + init { innerSpaceService .spaceListUpdate() diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceListUpdateProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceListUpdateProcessor.kt index f1193661ea..40310561e2 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceListUpdateProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceListUpdateProcessor.kt @@ -18,7 +18,6 @@ import timber.log.Timber internal class SpaceListUpdateProcessor( private val spaceRoomsFlow: MutableSharedFlow>, private val mapper: SpaceRoomMapper, - private val spaceRoomCache: SpaceRoomCache, ) { private val mutex = Mutex() @@ -37,7 +36,6 @@ internal class SpaceListUpdateProcessor( mutableListOf() } block(spaceRooms) - spaceRoomCache.update(spaceRooms) spaceRoomsFlow.emit(spaceRooms) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomCache.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomCache.kt deleted file mode 100644 index c8f55f1db6..0000000000 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomCache.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2025 New Vector 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.matrix.impl.spaces - -import io.element.android.libraries.core.coroutine.mapState -import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.spaces.SpaceRoom -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.update -import java.util.Optional - -/** - * An in memory cache of space rooms. - * Only caches Rooms with roomType [io.element.android.libraries.matrix.api.room.RoomType.Space]. - */ -class SpaceRoomCache { - private val inMemoryCache = MutableStateFlow>(emptyMap()) - fun getSpaceRoomFlow(roomId: RoomId): StateFlow> { - return inMemoryCache.mapState { Optional.ofNullable(it[roomId]) } - } - - fun update(spaceRooms: List) { - inMemoryCache.update { currentValues -> - val newValues = spaceRooms - .filter { it.isSpace } - .associateBy { it.roomId } - currentValues + newValues - } - } -} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomListExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomListExtensions.kt index 3d1a002e4d..c7107f745f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomListExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomListExtensions.kt @@ -16,11 +16,14 @@ import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.catch import org.matrix.rustcomponents.sdk.SpaceListUpdate +import org.matrix.rustcomponents.sdk.SpaceRoom import org.matrix.rustcomponents.sdk.SpaceRoomListEntriesListener import org.matrix.rustcomponents.sdk.SpaceRoomListInterface import org.matrix.rustcomponents.sdk.SpaceRoomListPaginationStateListener +import org.matrix.rustcomponents.sdk.SpaceRoomListSpaceListener import timber.log.Timber import uniffi.matrix_sdk_ui.SpaceRoomListPaginationState +import java.util.Optional internal fun SpaceRoomListInterface.paginationStateFlow(): Flow = callbackFlow { val listener = object : SpaceRoomListPaginationStateListener { @@ -55,3 +58,21 @@ internal fun SpaceRoomListInterface.spaceListUpdateFlow(): Flow> = + callbackFlow { + val listener = object : SpaceRoomListSpaceListener { + override fun onUpdate(space: SpaceRoom?) { + trySendBlocking(Optional.ofNullable(space)) + } + } + Timber.d("Open spaceUpdateFlow for SpaceRoomListInterface ${this@spaceUpdateFlow}") + trySendBlocking(Optional.ofNullable(space())) + val taskHandle = subscribeToSpaceUpdates(listener) + awaitClose { + Timber.d("Close spaceUpdateFlow for SpaceRoomListInterface ${this@spaceUpdateFlow}") + taskHandle.cancelAndDestroy() + } + }.catch { + Timber.d(it, "spaceUpdateFlow() failed") + }.buffer(Channel.UNLIMITED) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomMapper.kt index c217f941c7..3a9b78c3f9 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomMapper.kt @@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.spaces.SpaceRoom import io.element.android.libraries.matrix.impl.room.join.map import io.element.android.libraries.matrix.impl.room.map +import kotlinx.collections.immutable.toImmutableList import org.matrix.rustcomponents.sdk.SpaceRoom as RustSpaceRoom class SpaceRoomMapper { @@ -22,15 +23,18 @@ class SpaceRoomMapper { canonicalAlias = spaceRoom.canonicalAlias?.let(::RoomAlias), childrenCount = spaceRoom.childrenCount.toInt(), guestCanJoin = spaceRoom.guestCanJoin, - heroes = spaceRoom.heroes.orEmpty().map { it.map() }, + heroes = spaceRoom.heroes.orEmpty().map { it.map() }.toImmutableList(), joinRule = spaceRoom.joinRule?.map(), - name = spaceRoom.name, + rawName = spaceRoom.rawName, + displayName = spaceRoom.displayName, numJoinedMembers = spaceRoom.numJoinedMembers.toInt(), roomId = RoomId(spaceRoom.roomId), roomType = spaceRoom.roomType.map(), state = spaceRoom.state?.map(), topic = spaceRoom.topic, worldReadable = spaceRoom.worldReadable.orFalse(), + via = spaceRoom.via.toImmutableList(), + isDirect = spaceRoom.isDirect, ) } } 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 0d4363f7d1..1b941009e0 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 @@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershi import io.element.android.libraries.matrix.api.timeline.item.event.StateContent import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent +import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent import io.element.android.libraries.matrix.api.timeline.item.event.UtdCause import io.element.android.libraries.matrix.impl.media.map import io.element.android.libraries.matrix.impl.poll.map @@ -126,6 +127,7 @@ class TimelineEventContentMapper( source = kind.source.map(), ) } + is MsgLikeKind.Other -> UnknownContent } } is TimelineItemContent.ProfileChange -> { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapper.kt index 5b2e8fa9a2..172f3c4cb0 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapper.kt @@ -8,13 +8,16 @@ package io.element.android.libraries.matrix.impl.usersearch import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults +import io.element.android.libraries.matrix.impl.mapper.map import kotlinx.collections.immutable.toImmutableList import org.matrix.rustcomponents.sdk.SearchUsersResults object UserSearchResultMapper { fun map(result: SearchUsersResults): MatrixSearchUserResults { return MatrixSearchUserResults( - results = result.results.map(UserProfileMapper::map).toImmutableList(), + results = result.results + .map { userProfile -> userProfile.map() } + .toImmutableList(), limited = result.limited, ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/SessionVerificationRequestDetails.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/SessionVerificationRequestDetails.kt index d2e15a3fac..cd398fbfc1 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/SessionVerificationRequestDetails.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/SessionVerificationRequestDetails.kt @@ -12,22 +12,17 @@ import io.element.android.libraries.matrix.api.core.FlowId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails import io.element.android.libraries.matrix.api.verification.VerificationRequest +import io.element.android.libraries.matrix.impl.mapper.map import org.matrix.rustcomponents.sdk.SessionVerificationRequestDetails as RustSessionVerificationRequestDetails -import org.matrix.rustcomponents.sdk.UserProfile as RustUserProfile fun RustSessionVerificationRequestDetails.map() = SessionVerificationRequestDetails( senderProfile = senderProfile.map(), flowId = FlowId(flowId), deviceId = DeviceId(deviceId), + deviceDisplayName = deviceDisplayName, firstSeenTimestamp = firstSeenTimestamp.toLong(), ) -fun RustUserProfile.map() = SessionVerificationRequestDetails.SenderProfile( - userId = UserId(userId), - displayName = displayName, - avatarUrl = avatarUrl, -) - fun RustSessionVerificationRequestDetails.toVerificationRequest(currentUserId: UserId): VerificationRequest.Incoming { val details = map() return if (currentUserId == details.senderProfile.userId) { diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactoryTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactoryTest.kt index 1cb56dcbeb..92f9438322 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactoryTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactoryTest.kt @@ -36,12 +36,12 @@ class RustMatrixClientFactoryTest { } fun TestScope.createRustMatrixClientFactory( - baseDirectory: File = File("/base"), cacheDirectory: File = File("/cache"), - sessionStore: SessionStore = InMemorySessionStore(), + sessionStore: SessionStore = InMemorySessionStore( + updateUserProfileResult = { _, _, _ -> }, + ), clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(), ) = RustMatrixClientFactory( - baseDirectory = baseDirectory, cacheDirectory = cacheDirectory, appCoroutineScope = backgroundScope, coroutineDispatchers = testCoroutineDispatchers(), diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientTest.kt index 99f165eafb..dec0f4cd4f 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientTest.kt @@ -5,6 +5,8 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package io.element.android.libraries.matrix.impl import com.google.common.truth.Truth.assertThat @@ -12,17 +14,24 @@ import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiClient import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiSyncService import io.element.android.libraries.matrix.impl.room.FakeTimelineEventTypeFilterFactory +import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.A_DEVICE_ID import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.test.InMemorySessionStore +import io.element.android.libraries.sessionstorage.test.aSessionData 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.testCoroutineDispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Test import org.matrix.rustcomponents.sdk.Client +import org.matrix.rustcomponents.sdk.UserProfile import java.io.File class RustMatrixClientTest { @@ -51,12 +60,48 @@ class RustMatrixClientTest { client.destroy() } + @Test + fun `retrieving the UserProfile updates the database`() = runTest { + val updateUserProfileResult = lambdaRecorder { _, _, _ -> } + val sessionStore = InMemorySessionStore( + initialList = listOf( + aSessionData( + sessionId = A_USER_ID.value, + userDisplayName = null, + userAvatarUrl = null, + ) + ), + updateUserProfileResult = updateUserProfileResult, + ) + val client = createRustMatrixClient( + client = FakeFfiClient( + getProfileResult = { userId -> + UserProfile( + userId = userId, + displayName = A_USER_NAME, + avatarUrl = AN_AVATAR_URL, + ) + }, + ), + sessionStore = sessionStore, + ) + advanceUntilIdle() + updateUserProfileResult.assertions().isCalledOnce() + .with( + value(A_USER_ID.value), + value(A_USER_NAME), + value(AN_AVATAR_URL), + ) + client.destroy() + } + private fun TestScope.createRustMatrixClient( client: Client = FakeFfiClient(), - sessionStore: SessionStore = InMemorySessionStore(), + sessionStore: SessionStore = InMemorySessionStore( + updateUserProfileResult = { _, _, _ -> }, + ), ) = RustMatrixClient( innerClient = client, - baseDirectory = File(""), sessionStore = sessionStore, appCoroutineScope = backgroundScope, sessionDelegate = aRustClientSessionDelegate( diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt index 1cb5db91f6..490c921aa9 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt @@ -53,7 +53,6 @@ class RustMatrixAuthenticationServiceTest { val baseDirectory = File("/base") val cacheDirectory = File("/cache") val rustMatrixClientFactory = createRustMatrixClientFactory( - baseDirectory = baseDirectory, cacheDirectory = cacheDirectory, sessionStore = sessionStore, clientBuilderProvider = clientBuilderProvider, diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/SpaceRoom.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/SpaceRoom.kt index b9f665add1..58a86ffe2e 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/SpaceRoom.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/SpaceRoom.kt @@ -19,7 +19,8 @@ fun aRustSpaceRoom( roomId: RoomId = A_ROOM_ID, isDirect: Boolean = false, canonicalAlias: String? = null, - name: String? = null, + rawName: String? = null, + displayName: String = "", topic: String? = null, avatarUrl: String? = null, roomType: RoomType = RoomType.Space, @@ -34,7 +35,8 @@ fun aRustSpaceRoom( roomId = roomId.value, isDirect = isDirect, canonicalAlias = canonicalAlias, - name = name, + rawName = rawName, + displayName = displayName, topic = topic, avatarUrl = avatarUrl, roomType = roomType, diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt index 8225ce5ebf..3f53d9d5e2 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt @@ -42,6 +42,7 @@ class FakeFfiClient( private val session: Session = aRustSession(), private val clearCachesResult: () -> Unit = { lambdaError() }, private val withUtdHook: (UnableToDecryptDelegate) -> Unit = { lambdaError() }, + private val getProfileResult: (String) -> UserProfile = { UserProfile(userId = userId, displayName = null, avatarUrl = null) }, private val homeserverLoginDetailsResult: () -> HomeserverLoginDetails = { lambdaError() }, private val closeResult: () -> Unit = {}, ) : Client(NoPointer) { @@ -79,7 +80,7 @@ class FakeFfiClient( } override suspend fun getProfile(userId: String): UserProfile { - return UserProfile(userId = userId, displayName = null, avatarUrl = null) + return getProfileResult(userId) } override suspend fun homeserverLoginDetails(): HomeserverLoginDetails { 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 87614304f3..d2dce3816c 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,6 +42,5 @@ class FakeFfiClientBuilder( override fun username(username: String) = this override fun enableShareHistoryOnInvite(enableShareHistoryOnInvite: Boolean): ClientBuilder = this override fun threadsEnabled(enabled: Boolean, threadSubscriptions: Boolean): ClientBuilder = this - override suspend fun build() = buildResult() } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiEncryption.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiEncryption.kt index 536288caef..177ab5b251 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiEncryption.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiEncryption.kt @@ -32,6 +32,10 @@ class FakeFfiEncryption : Encryption(NoPointer) { return false } + override suspend fun hasDevicesToVerifyAgainst(): Boolean { + return true + } + override fun backupState(): BackupState { return BackupState.ENABLED } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserProfileMapperTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/mapper/UserProfileMapperTest.kt similarity index 78% rename from libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserProfileMapperTest.kt rename to libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/mapper/UserProfileMapperTest.kt index e04b0f26d5..ea2d971056 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserProfileMapperTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/mapper/UserProfileMapperTest.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.impl.usersearch +package io.element.android.libraries.matrix.impl.mapper import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.user.MatrixUser @@ -16,7 +16,7 @@ import org.junit.Test class UserProfileMapperTest { @Test fun map() { - assertThat(UserProfileMapper.map(aRustUserProfile(A_USER_ID.value, "displayName", "avatarUrl"))) + assertThat(aRustUserProfile(A_USER_ID.value, "displayName", "avatarUrl").map()) .isEqualTo(MatrixUser(A_USER_ID, "displayName", "avatarUrl")) } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/MessageEventTypeKtTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/MessageEventTypeKtTest.kt index 75936ca0dc..02e503c56e 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/MessageEventTypeKtTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/MessageEventTypeKtTest.kt @@ -15,55 +15,55 @@ import org.matrix.rustcomponents.sdk.MessageLikeEventType class MessageEventTypeKtTest { @Test fun `map Rust type should result to correct Kotlin type`() { - assertThat(MessageLikeEventType.CALL_ANSWER.map()).isEqualTo(MessageEventType.CALL_ANSWER) - assertThat(MessageLikeEventType.CALL_INVITE.map()).isEqualTo(MessageEventType.CALL_INVITE) - assertThat(MessageLikeEventType.CALL_HANGUP.map()).isEqualTo(MessageEventType.CALL_HANGUP) - assertThat(MessageLikeEventType.CALL_CANDIDATES.map()).isEqualTo(MessageEventType.CALL_CANDIDATES) - assertThat(MessageLikeEventType.RTC_NOTIFICATION.map()).isEqualTo(MessageEventType.RTC_NOTIFICATION) - assertThat(MessageLikeEventType.KEY_VERIFICATION_READY.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_READY) - assertThat(MessageLikeEventType.KEY_VERIFICATION_START.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_START) - assertThat(MessageLikeEventType.KEY_VERIFICATION_CANCEL.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_CANCEL) - assertThat(MessageLikeEventType.KEY_VERIFICATION_ACCEPT.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_ACCEPT) - assertThat(MessageLikeEventType.KEY_VERIFICATION_KEY.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_KEY) - assertThat(MessageLikeEventType.KEY_VERIFICATION_MAC.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_MAC) - assertThat(MessageLikeEventType.KEY_VERIFICATION_DONE.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_DONE) - assertThat(MessageLikeEventType.REACTION.map()).isEqualTo(MessageEventType.REACTION) - assertThat(MessageLikeEventType.ROOM_ENCRYPTED.map()).isEqualTo(MessageEventType.ROOM_ENCRYPTED) - assertThat(MessageLikeEventType.ROOM_MESSAGE.map()).isEqualTo(MessageEventType.ROOM_MESSAGE) - assertThat(MessageLikeEventType.ROOM_REDACTION.map()).isEqualTo(MessageEventType.ROOM_REDACTION) - assertThat(MessageLikeEventType.STICKER.map()).isEqualTo(MessageEventType.STICKER) - assertThat(MessageLikeEventType.POLL_END.map()).isEqualTo(MessageEventType.POLL_END) - assertThat(MessageLikeEventType.POLL_RESPONSE.map()).isEqualTo(MessageEventType.POLL_RESPONSE) - assertThat(MessageLikeEventType.POLL_START.map()).isEqualTo(MessageEventType.POLL_START) - assertThat(MessageLikeEventType.UNSTABLE_POLL_END.map()).isEqualTo(MessageEventType.UNSTABLE_POLL_END) - assertThat(MessageLikeEventType.UNSTABLE_POLL_RESPONSE.map()).isEqualTo(MessageEventType.UNSTABLE_POLL_RESPONSE) - assertThat(MessageLikeEventType.UNSTABLE_POLL_START.map()).isEqualTo(MessageEventType.UNSTABLE_POLL_START) + assertThat(MessageLikeEventType.CallAnswer.map()).isEqualTo(MessageEventType.CallAnswer) + assertThat(MessageLikeEventType.CallInvite.map()).isEqualTo(MessageEventType.CallInvite) + assertThat(MessageLikeEventType.CallHangup.map()).isEqualTo(MessageEventType.CallHangup) + assertThat(MessageLikeEventType.CallCandidates.map()).isEqualTo(MessageEventType.CallCandidates) + assertThat(MessageLikeEventType.RtcNotification.map()).isEqualTo(MessageEventType.RtcNotification) + assertThat(MessageLikeEventType.KeyVerificationReady.map()).isEqualTo(MessageEventType.KeyVerificationReady) + assertThat(MessageLikeEventType.KeyVerificationStart.map()).isEqualTo(MessageEventType.KeyVerificationStart) + assertThat(MessageLikeEventType.KeyVerificationCancel.map()).isEqualTo(MessageEventType.KeyVerificationCancel) + assertThat(MessageLikeEventType.KeyVerificationAccept.map()).isEqualTo(MessageEventType.KeyVerificationAccept) + assertThat(MessageLikeEventType.KeyVerificationKey.map()).isEqualTo(MessageEventType.KeyVerificationKey) + assertThat(MessageLikeEventType.KeyVerificationMac.map()).isEqualTo(MessageEventType.KeyVerificationMac) + assertThat(MessageLikeEventType.KeyVerificationDone.map()).isEqualTo(MessageEventType.KeyVerificationDone) + assertThat(MessageLikeEventType.Reaction.map()).isEqualTo(MessageEventType.Reaction) + assertThat(MessageLikeEventType.RoomEncrypted.map()).isEqualTo(MessageEventType.RoomEncrypted) + assertThat(MessageLikeEventType.RoomMessage.map()).isEqualTo(MessageEventType.RoomMessage) + assertThat(MessageLikeEventType.RoomRedaction.map()).isEqualTo(MessageEventType.RoomRedaction) + assertThat(MessageLikeEventType.Sticker.map()).isEqualTo(MessageEventType.Sticker) + assertThat(MessageLikeEventType.PollEnd.map()).isEqualTo(MessageEventType.PollEnd) + assertThat(MessageLikeEventType.PollResponse.map()).isEqualTo(MessageEventType.PollResponse) + assertThat(MessageLikeEventType.PollStart.map()).isEqualTo(MessageEventType.PollStart) + assertThat(MessageLikeEventType.UnstablePollEnd.map()).isEqualTo(MessageEventType.UnstablePollEnd) + assertThat(MessageLikeEventType.UnstablePollResponse.map()).isEqualTo(MessageEventType.UnstablePollResponse) + assertThat(MessageLikeEventType.UnstablePollStart.map()).isEqualTo(MessageEventType.UnstablePollStart) } @Test fun `map Kotlin type should result to correct Rust type`() { - assertThat(MessageEventType.CALL_ANSWER.map()).isEqualTo(MessageLikeEventType.CALL_ANSWER) - assertThat(MessageEventType.CALL_INVITE.map()).isEqualTo(MessageLikeEventType.CALL_INVITE) - assertThat(MessageEventType.CALL_HANGUP.map()).isEqualTo(MessageLikeEventType.CALL_HANGUP) - assertThat(MessageEventType.CALL_CANDIDATES.map()).isEqualTo(MessageLikeEventType.CALL_CANDIDATES) - assertThat(MessageEventType.RTC_NOTIFICATION.map()).isEqualTo(MessageLikeEventType.RTC_NOTIFICATION) - assertThat(MessageEventType.KEY_VERIFICATION_READY.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_READY) - assertThat(MessageEventType.KEY_VERIFICATION_START.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_START) - assertThat(MessageEventType.KEY_VERIFICATION_CANCEL.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_CANCEL) - assertThat(MessageEventType.KEY_VERIFICATION_ACCEPT.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_ACCEPT) - assertThat(MessageEventType.KEY_VERIFICATION_KEY.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_KEY) - assertThat(MessageEventType.KEY_VERIFICATION_MAC.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_MAC) - assertThat(MessageEventType.KEY_VERIFICATION_DONE.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_DONE) - assertThat(MessageEventType.REACTION.map()).isEqualTo(MessageLikeEventType.REACTION) - assertThat(MessageEventType.ROOM_ENCRYPTED.map()).isEqualTo(MessageLikeEventType.ROOM_ENCRYPTED) - assertThat(MessageEventType.ROOM_MESSAGE.map()).isEqualTo(MessageLikeEventType.ROOM_MESSAGE) - assertThat(MessageEventType.ROOM_REDACTION.map()).isEqualTo(MessageLikeEventType.ROOM_REDACTION) - assertThat(MessageEventType.STICKER.map()).isEqualTo(MessageLikeEventType.STICKER) - assertThat(MessageEventType.POLL_END.map()).isEqualTo(MessageLikeEventType.POLL_END) - assertThat(MessageEventType.POLL_RESPONSE.map()).isEqualTo(MessageLikeEventType.POLL_RESPONSE) - assertThat(MessageEventType.POLL_START.map()).isEqualTo(MessageLikeEventType.POLL_START) - assertThat(MessageEventType.UNSTABLE_POLL_END.map()).isEqualTo(MessageLikeEventType.UNSTABLE_POLL_END) - assertThat(MessageEventType.UNSTABLE_POLL_RESPONSE.map()).isEqualTo(MessageLikeEventType.UNSTABLE_POLL_RESPONSE) - assertThat(MessageEventType.UNSTABLE_POLL_START.map()).isEqualTo(MessageLikeEventType.UNSTABLE_POLL_START) + assertThat(MessageEventType.CallAnswer.map()).isEqualTo(MessageLikeEventType.CallAnswer) + assertThat(MessageEventType.CallInvite.map()).isEqualTo(MessageLikeEventType.CallInvite) + assertThat(MessageEventType.CallHangup.map()).isEqualTo(MessageLikeEventType.CallHangup) + assertThat(MessageEventType.CallCandidates.map()).isEqualTo(MessageLikeEventType.CallCandidates) + assertThat(MessageEventType.RtcNotification.map()).isEqualTo(MessageLikeEventType.RtcNotification) + assertThat(MessageEventType.KeyVerificationReady.map()).isEqualTo(MessageLikeEventType.KeyVerificationReady) + assertThat(MessageEventType.KeyVerificationStart.map()).isEqualTo(MessageLikeEventType.KeyVerificationStart) + assertThat(MessageEventType.KeyVerificationCancel.map()).isEqualTo(MessageLikeEventType.KeyVerificationCancel) + assertThat(MessageEventType.KeyVerificationAccept.map()).isEqualTo(MessageLikeEventType.KeyVerificationAccept) + assertThat(MessageEventType.KeyVerificationKey.map()).isEqualTo(MessageLikeEventType.KeyVerificationKey) + assertThat(MessageEventType.KeyVerificationMac.map()).isEqualTo(MessageLikeEventType.KeyVerificationMac) + assertThat(MessageEventType.KeyVerificationDone.map()).isEqualTo(MessageLikeEventType.KeyVerificationDone) + assertThat(MessageEventType.Reaction.map()).isEqualTo(MessageLikeEventType.Reaction) + assertThat(MessageEventType.RoomEncrypted.map()).isEqualTo(MessageLikeEventType.RoomEncrypted) + assertThat(MessageEventType.RoomMessage.map()).isEqualTo(MessageLikeEventType.RoomMessage) + assertThat(MessageEventType.RoomRedaction.map()).isEqualTo(MessageLikeEventType.RoomRedaction) + assertThat(MessageEventType.Sticker.map()).isEqualTo(MessageLikeEventType.Sticker) + assertThat(MessageEventType.PollEnd.map()).isEqualTo(MessageLikeEventType.PollEnd) + assertThat(MessageEventType.PollResponse.map()).isEqualTo(MessageLikeEventType.PollResponse) + assertThat(MessageEventType.PollStart.map()).isEqualTo(MessageLikeEventType.PollStart) + assertThat(MessageEventType.UnstablePollEnd.map()).isEqualTo(MessageLikeEventType.UnstablePollEnd) + assertThat(MessageEventType.UnstablePollResponse.map()).isEqualTo(MessageLikeEventType.UnstablePollResponse) + assertThat(MessageEventType.UnstablePollStart.map()).isEqualTo(MessageLikeEventType.UnstablePollStart) } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapperTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapperTest.kt index 90ff2b9211..a95720baef 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapperTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapperTest.kt @@ -8,9 +8,6 @@ package io.element.android.libraries.matrix.impl.room import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.matrix.api.core.EventId -import io.element.android.libraries.matrix.api.core.RoomAlias -import io.element.android.libraries.matrix.api.core.UserId 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.room.RoomNotificationMode @@ -34,7 +31,6 @@ import io.element.android.libraries.matrix.test.room.defaultRoomPowerLevelValues import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toPersistentList import org.junit.Test import org.matrix.rustcomponents.sdk.Membership import uniffi.matrix_sdk_base.EncryptionState @@ -113,15 +109,15 @@ class RoomInfoMapperTest { notificationCount = 11L, userDefinedNotificationMode = RoomNotificationMode.MUTE, hasRoomCall = true, - activeRoomCallParticipants = listOf(A_USER_ID_3).toImmutableList(), - heroes = listOf( + activeRoomCallParticipants = persistentListOf(A_USER_ID_3), + heroes = persistentListOf( MatrixUser( userId = A_USER_ID, displayName = "displayName", avatarUrl = "avatarUrl", ) - ).toImmutableList(), - pinnedEventIds = listOf(AN_EVENT_ID).toPersistentList(), + ), + pinnedEventIds = persistentListOf(AN_EVENT_ID), creators = persistentListOf(A_USER_ID), isMarkedUnread = false, numUnreadMessages = 12L, @@ -191,7 +187,7 @@ class RoomInfoMapperTest { successorRoom = null, isFavorite = true, canonicalAlias = null, - alternativeAliases = emptyList().toPersistentList(), + alternativeAliases = persistentListOf(), currentUserMembership = CurrentUserMembership.INVITED, inviter = null, activeMembersCount = 2L, @@ -205,9 +201,9 @@ class RoomInfoMapperTest { notificationCount = 11L, userDefinedNotificationMode = null, hasRoomCall = false, - activeRoomCallParticipants = emptyList().toImmutableList(), - heroes = emptyList().toImmutableList(), - pinnedEventIds = emptyList().toPersistentList(), + activeRoomCallParticipants = persistentListOf(), + heroes = persistentListOf(), + pinnedEventIds = persistentListOf(), creators = persistentListOf(), isMarkedUnread = true, numUnreadMessages = 12L, diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoomTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoomTest.kt index e24eca53cb..50d6c348b5 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoomTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoomTest.kt @@ -57,6 +57,7 @@ class RustBaseRoomTest { leaveRoomAndObserveMembershipChange(roomMembershipObserver, rustBaseRoom) { val membershipUpdate = awaitItem() assertThat(membershipUpdate.roomId).isEqualTo(rustBaseRoom.roomId) + assertThat(membershipUpdate.isSpace).isFalse() assertThat(membershipUpdate.isUserInRoom).isFalse() assertThat(membershipUpdate.change).isEqualTo(MembershipChange.LEFT) } @@ -77,6 +78,7 @@ class RustBaseRoomTest { leaveRoomAndObserveMembershipChange(roomMembershipObserver, rustBaseRoom) { val membershipUpdate = awaitItem() assertThat(membershipUpdate.roomId).isEqualTo(rustBaseRoom.roomId) + assertThat(membershipUpdate.isSpace).isFalse() assertThat(membershipUpdate.isUserInRoom).isFalse() assertThat(membershipUpdate.change).isEqualTo(MembershipChange.KNOCK_RETRACTED) } @@ -97,6 +99,7 @@ class RustBaseRoomTest { leaveRoomAndObserveMembershipChange(roomMembershipObserver, rustBaseRoom) { val membershipUpdate = awaitItem() assertThat(membershipUpdate.roomId).isEqualTo(rustBaseRoom.roomId) + assertThat(membershipUpdate.isSpace).isFalse() assertThat(membershipUpdate.isUserInRoom).isFalse() assertThat(membershipUpdate.change).isEqualTo(MembershipChange.INVITATION_REJECTED) } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilterTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilterTest.kt index ff4e5b29e5..932f9ba6be 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilterTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilterTest.kt @@ -41,6 +41,14 @@ class RoomListFilterTest { currentUserMembership = CurrentUserMembership.INVITED ) + private val space = aRoomSummary( + isSpace = true + ) + private val invitedSpace = aRoomSummary( + isSpace = true, + currentUserMembership = CurrentUserMembership.INVITED + ) + private val roomSummaries = listOf( regularRoom, dmRoom, @@ -49,13 +57,15 @@ class RoomListFilterTest { unreadNotificationRoom, roomToSearch, roomWithAccent, - invitedRoom + invitedRoom, + space, + invitedSpace, ) @Test fun `Room list filter all empty`() = runTest { val filter = RoomListFilter.all() - assertThat(roomSummaries.filter(filter)).isEqualTo(roomSummaries) + assertThat(roomSummaries.filter(filter)).isEqualTo(roomSummaries - space) } @Test @@ -83,6 +93,12 @@ class RoomListFilterTest { ) } + @Test + fun `Room list filter space`() = runTest { + val filter = RoomListFilter.Category.Space + assertThat(roomSummaries.filter(filter)).containsExactly(space, invitedSpace) + } + @Test fun `Room list filter favorite`() = runTest { val filter = RoomListFilter.Favorite @@ -98,7 +114,7 @@ class RoomListFilterTest { @Test fun `Room list filter invites`() = runTest { val filter = RoomListFilter.Invite - assertThat(roomSummaries.filter(filter)).containsExactly(invitedRoom) + assertThat(roomSummaries.filter(filter)).containsExactly(invitedRoom, invitedSpace) } @Test @@ -136,10 +152,4 @@ class RoomListFilterTest { ) assertThat(roomSummaries.filter(filter)).isEmpty() } - - @Test - fun `Room list filter all with empty list`() = runTest { - val filter = RoomListFilter.all() - assertThat(roomSummaries.filter(filter)).isEqualTo(roomSummaries) - } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/RoomSummaryListProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/RoomSummaryListProcessorTest.kt index 47c3a19a6b..ae05c7b4e9 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/RoomSummaryListProcessorTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/RoomSummaryListProcessorTest.kt @@ -185,6 +185,5 @@ class RoomSummaryListProcessorTest { ) = SpaceListUpdateProcessor( spaceRoomsFlow = spaceRoomsFlow, mapper = SpaceRoomMapper(), - spaceRoomCache = SpaceRoomCache(), ) } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceRoomListTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceRoomListTest.kt index 3d4cb98abc..7a494ae8c3 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceRoomListTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceRoomListTest.kt @@ -17,7 +17,6 @@ import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSpaceRoo import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiSpaceRoomList 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.previewutils.room.aSpaceRoom import io.element.android.tests.testutils.lambda.lambdaRecorder import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope @@ -26,7 +25,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Test import org.matrix.rustcomponents.sdk.SpaceListUpdate import uniffi.matrix_sdk_ui.SpaceRoomListPaginationState -import kotlin.jvm.optionals.getOrNull import org.matrix.rustcomponents.sdk.SpaceRoomList as InnerSpaceRoomList class RustSpaceRoomListTest { @@ -87,33 +85,17 @@ class RustSpaceRoomListTest { paginateResult.assertions().isCalledOnce() } - @Test - fun `currentSpaceFlow reads value from the SpaceRoomCache`() = runTest { - val spaceRoomCache = SpaceRoomCache() - val sut = createRustSpaceRoomList( - spaceRoomCache = spaceRoomCache, - ) - sut.currentSpaceFlow().test { - assertThat(awaitItem().getOrNull()).isNull() - val spaceRoom = aSpaceRoom(roomId = A_ROOM_ID) - spaceRoomCache.update(listOf(spaceRoom)) - assertThat(awaitItem().getOrNull()).isEqualTo(spaceRoom) - } - } - private fun TestScope.createRustSpaceRoomList( roomId: RoomId = A_ROOM_ID, innerSpaceRoomList: InnerSpaceRoomList = FakeFfiSpaceRoomList(), innerProvider: suspend () -> InnerSpaceRoomList = { innerSpaceRoomList }, spaceRoomMapper: SpaceRoomMapper = SpaceRoomMapper(), - spaceRoomCache: SpaceRoomCache = SpaceRoomCache(), ): RustSpaceRoomList { return RustSpaceRoomList( roomId = roomId, innerProvider = innerProvider, - sessionCoroutineScope = backgroundScope, + coroutineScope = backgroundScope, spaceRoomMapper = spaceRoomMapper, - spaceRoomCache = spaceRoomCache, ) } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomCacheTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomCacheTest.kt deleted file mode 100644 index 061169c5e8..0000000000 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomCacheTest.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2025 New Vector 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.matrix.impl.spaces - -import app.cash.turbine.test -import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.matrix.api.room.RoomType -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.previewutils.room.aSpaceRoom -import kotlinx.coroutines.test.runTest -import org.junit.Test - -class SpaceRoomCacheTest { - @Test - fun `getSpaceRoomFlow emits items`() = runTest { - val sut = SpaceRoomCache() - sut.getSpaceRoomFlow(A_ROOM_ID).test { - assertThat(awaitItem().isEmpty).isTrue() - val room = aSpaceRoom( - roomId = A_ROOM_ID, - roomType = RoomType.Room, - ) - sut.update(listOf(room)) - // Not a space, should not be cached - expectNoEvents() - val space = aSpaceRoom( - roomId = A_ROOM_ID, - roomType = RoomType.Space, - ) - sut.update(listOf(space)) - assertThat(awaitItem().get()).isEqualTo(space) - val spaceOther = aSpaceRoom( - roomId = A_ROOM_ID_2, - roomType = RoomType.Space, - ) - sut.update(listOf(spaceOther)) - expectNoEvents() - } - } -} diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 4f7d42e5fb..f0e296ea5d 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -32,6 +32,7 @@ import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryServic import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.spaces.SpaceService import io.element.android.libraries.matrix.api.sync.SlidingSyncVersion +import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.verification.SessionVerificationService @@ -68,15 +69,16 @@ class FakeMatrixClient( private val userAvatarUrl: String? = AN_AVATAR_URL, override val roomListService: RoomListService = FakeRoomListService(), override val spaceService: SpaceService = FakeSpaceService(), - override val mediaLoader: MatrixMediaLoader = FakeMatrixMediaLoader(), - private val sessionVerificationService: FakeSessionVerificationService = FakeSessionVerificationService(), - private val pushersService: FakePushersService = FakePushersService(), - private val notificationService: FakeNotificationService = FakeNotificationService(), - private val notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(), - private val syncService: FakeSyncService = FakeSyncService(), - private val encryptionService: FakeEncryptionService = FakeEncryptionService(), - private val roomDirectoryService: RoomDirectoryService = FakeRoomDirectoryService(), - private val mediaPreviewService: MediaPreviewService = FakeMediaPreviewService(), + override val matrixMediaLoader: MatrixMediaLoader = FakeMatrixMediaLoader(), + override val sessionVerificationService: SessionVerificationService = FakeSessionVerificationService(), + override val pushersService: PushersService = FakePushersService(), + override val notificationService: NotificationService = FakeNotificationService(), + override val notificationSettingsService: NotificationSettingsService = FakeNotificationSettingsService(), + override val syncService: SyncService = FakeSyncService(), + override val encryptionService: EncryptionService = FakeEncryptionService(), + override val roomDirectoryService: RoomDirectoryService = FakeRoomDirectoryService(), + override val mediaPreviewService: MediaPreviewService = FakeMediaPreviewService(), + override val roomMembershipObserver: RoomMembershipObserver = RoomMembershipObserver(), private val accountManagementUrlResult: (AccountManagementAction?) -> Result = { lambdaError() }, private val resolveRoomAliasResult: (RoomAlias) -> Result> = { Result.success( @@ -97,6 +99,8 @@ class FakeMatrixClient( override val ignoredUsersFlow: StateFlow> = MutableStateFlow(persistentListOf()), private val getMaxUploadSizeResult: () -> Result = { lambdaError() }, private val getJoinedRoomIdsResult: () -> Result> = { Result.success(emptySet()) }, + private val getRecentEmojisLambda: () -> Result> = { Result.success(emptyList()) }, + private val addRecentEmojiLambda: (String) -> Result = { Result.success(Unit) }, ) : MatrixClient { var setDisplayNameCalled: Boolean = false private set @@ -172,10 +176,6 @@ class FakeMatrixClient( return searchUserResults[searchTerm] ?: Result.failure(IllegalStateException("No response defined for $searchTerm")) } - override fun syncService() = syncService - - override fun roomDirectoryService() = roomDirectoryService - override suspend fun getCacheSize(): Long { return 0 } @@ -236,19 +236,6 @@ class FakeMatrixClient( return knockRoomLambda(roomIdOrAlias, message, serverNames) } - override fun sessionVerificationService(): SessionVerificationService = sessionVerificationService - - override fun pushersService(): PushersService = pushersService - - override fun notificationService(): NotificationService = notificationService - override fun notificationSettingsService(): NotificationSettingsService = notificationSettingsService - override fun encryptionService(): EncryptionService = encryptionService - override fun mediaPreviewService(): MediaPreviewService = mediaPreviewService - - override fun roomMembershipObserver(): RoomMembershipObserver { - return RoomMembershipObserver() - } - // Mocks fun givenCreateRoomResult(result: Result) { @@ -349,4 +336,12 @@ class FakeMatrixClient( override suspend fun getMaxFileUploadSize(): Result { return getMaxUploadSizeResult() } + + override suspend fun addRecentEmoji(emoji: String): Result { + return addRecentEmojiLambda(emoji) + } + + override suspend fun getRecentEmojis(): Result> { + return getRecentEmojisLambda() + } } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt index ca0db64285..f05f6958c0 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt @@ -26,6 +26,7 @@ const val A_PASSWORD = "password" const val A_PASSPHRASE = "passphrase" const val A_SECRET = "secret" const val AN_APPLICATION_NAME = "AppName" +const val AN_APPLICATION_NAME_DESKTOP = "AppNameDesktop" val A_USER_ID = UserId("@alice:server.org") val A_USER_ID_2 = UserId("@bob:server.org") @@ -65,6 +66,8 @@ const val ANOTHER_MESSAGE = "Hello universe!" const val A_CAPTION = "A media caption" const val A_REASON = "A reason" +const val A_SPACE_NAME = "A space name" + const val A_REDACTION_REASON = "A redaction reason" const val A_HOMESERVER_URL = "matrix.org" diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt index 7226465772..b4199ff8e4 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt @@ -34,6 +34,7 @@ class FakeEncryptionService( override val recoveryStateStateFlow: MutableStateFlow = MutableStateFlow(RecoveryState.UNKNOWN) override val enableRecoveryProgressStateFlow: MutableStateFlow = MutableStateFlow(EnableRecoveryProgress.Starting) override val isLastDevice: MutableStateFlow = MutableStateFlow(false) + override val hasDevicesToVerifyAgainst: MutableStateFlow = MutableStateFlow(true) private var waitForBackupUploadSteadyStateFlow: Flow = flowOf() private var recoverFailure: Exception? = null @@ -83,6 +84,10 @@ class FakeEncryptionService( this.isLastDevice.value = isLastDevice } + fun emitHasDevicesToVerifyAgainst(hasDevicesToVerifyAgainst: Boolean) { + this.hasDevicesToVerifyAgainst.value = hasDevicesToVerifyAgainst + } + override suspend fun resetRecoveryKey(): Result = simulateLongTask { return Result.success(FAKE_RECOVERY_KEY) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeBaseRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeBaseRoom.kt index 3ec9c3f870..31309beecf 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeBaseRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeBaseRoom.kt @@ -71,6 +71,7 @@ class FakeBaseRoom( private val forgetResult: () -> Result = { lambdaError() }, private val reportRoomResult: (String?) -> Result = { lambdaError() }, private val predecessorRoomResult: () -> PredecessorRoom? = { null }, + private val threadRootIdForEventResult: (EventId) -> Result = { lambdaError() }, ) : BaseRoom { private val _roomInfoFlow: MutableStateFlow = MutableStateFlow(initialRoomInfo) override val roomInfoFlow: StateFlow = _roomInfoFlow @@ -244,6 +245,10 @@ class FakeBaseRoom( fun givenUpdateMembersResult(result: () -> Unit) { updateMembersResult = result } + + override suspend fun threadRootIdForEvent(eventId: EventId): Result { + return threadRootIdForEventResult(eventId) + } } fun defaultRoomPowerLevelValues() = RoomPowerLevelsValues( diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt index 9c16319d65..c15d29eff8 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt @@ -32,7 +32,6 @@ import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toPersistentList fun aRoomSummary( info: RoomInfo = aRoomInfo(), @@ -109,7 +108,7 @@ fun aRoomSummary( userDefinedNotificationMode = userDefinedNotificationMode, hasRoomCall = hasRoomCall, activeRoomCallParticipants = activeRoomCallParticipants.toImmutableList(), - heroes = heroes.toPersistentList(), + heroes = heroes.toImmutableList(), pinnedEventIds = pinnedEventIds.toImmutableList(), creators = roomCreators.toImmutableList(), isMarkedUnread = isMarkedUnread, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/spaces/FakeLeaveSpaceHandle.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/spaces/FakeLeaveSpaceHandle.kt new file mode 100644 index 0000000000..b19d3f1344 --- /dev/null +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/spaces/FakeLeaveSpaceHandle.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2025 New Vector 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.matrix.test.spaces + +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.spaces.LeaveSpaceHandle +import io.element.android.libraries.matrix.api.spaces.LeaveSpaceRoom +import io.element.android.libraries.matrix.test.A_SPACE_ID +import io.element.android.tests.testutils.lambda.lambdaError +import io.element.android.tests.testutils.simulateLongTask + +class FakeLeaveSpaceHandle( + override val id: RoomId = A_SPACE_ID, + private val roomsResult: () -> Result> = { lambdaError() }, + private val leaveResult: (List) -> Result = { lambdaError() }, + private val closeResult: () -> Unit = { lambdaError() }, +) : LeaveSpaceHandle { + override suspend fun rooms(): Result> = simulateLongTask { + roomsResult() + } + + override suspend fun leave(roomIds: List): Result = simulateLongTask { + leaveResult(roomIds) + } + + override fun close() { + return closeResult() + } +} diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/spaces/FakeSpaceRoomList.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/spaces/FakeSpaceRoomList.kt index 3579f162b9..6eac3dd0f4 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/spaces/FakeSpaceRoomList.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/spaces/FakeSpaceRoomList.kt @@ -7,8 +7,10 @@ package io.element.android.libraries.matrix.test.spaces +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.spaces.SpaceRoom import io.element.android.libraries.matrix.api.spaces.SpaceRoomList +import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.simulateLongTask import kotlinx.coroutines.flow.Flow @@ -18,13 +20,14 @@ import kotlinx.coroutines.flow.asStateFlow import java.util.Optional class FakeSpaceRoomList( + override val roomId: RoomId = A_ROOM_ID, initialSpaceFlowValue: SpaceRoom? = null, initialSpaceRoomsValue: List = emptyList(), initialSpaceRoomList: SpaceRoomList.PaginationStatus = SpaceRoomList.PaginationStatus.Loading, private val paginateResult: () -> Result = { lambdaError() }, ) : SpaceRoomList { private val currentSpaceMutableStateFlow: MutableStateFlow> = MutableStateFlow(Optional.ofNullable(initialSpaceFlowValue)) - override fun currentSpaceFlow(): StateFlow> = currentSpaceMutableStateFlow.asStateFlow() + override val currentSpaceFlow: StateFlow> = currentSpaceMutableStateFlow.asStateFlow() fun emitCurrentSpace(value: SpaceRoom?) { currentSpaceMutableStateFlow.value = Optional.ofNullable(value) @@ -47,4 +50,8 @@ class FakeSpaceRoomList( override suspend fun paginate(): Result = simulateLongTask { paginateResult() } + + override fun destroy() { + // No op + } } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/spaces/FakeSpaceService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/spaces/FakeSpaceService.kt index 43cc8ae8d2..d768b175d0 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/spaces/FakeSpaceService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/spaces/FakeSpaceService.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.matrix.test.spaces import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.spaces.LeaveSpaceHandle import io.element.android.libraries.matrix.api.spaces.SpaceRoom import io.element.android.libraries.matrix.api.spaces.SpaceRoomList import io.element.android.libraries.matrix.api.spaces.SpaceService @@ -20,6 +21,7 @@ import kotlinx.coroutines.flow.asSharedFlow class FakeSpaceService( private val joinedSpacesResult: () -> Result> = { lambdaError() }, private val spaceRoomListResult: (RoomId) -> SpaceRoomList = { lambdaError() }, + private val leaveSpaceHandleResult: (RoomId) -> LeaveSpaceHandle = { lambdaError() }, ) : SpaceService { private val _spaceRoomsFlow = MutableSharedFlow>() override val spaceRoomsFlow: SharedFlow> @@ -36,4 +38,8 @@ class FakeSpaceService( override fun spaceRoomList(id: RoomId): SpaceRoomList { return spaceRoomListResult(id) } + + override fun getLeaveSpaceHandle(spaceId: RoomId): LeaveSpaceHandle { + return leaveSpaceHandleResult(spaceId) + } } diff --git a/libraries/matrixui/build.gradle.kts b/libraries/matrixui/build.gradle.kts index a500186587..96045f3a93 100644 --- a/libraries/matrixui/build.gradle.kts +++ b/libraries/matrixui/build.gradle.kts @@ -37,6 +37,7 @@ dependencies { implementation(libs.coil.gif) implementation(libs.coil.network.okhttp) implementation(libs.jsoup) + implementation(projects.libraries.previewutils) testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/JoinButton.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/JoinButton.kt new file mode 100644 index 0000000000..0d5b94dc64 --- /dev/null +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/JoinButton.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2025 New Vector 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.matrix.ui.components + +import androidx.compose.material3.LocalContentColor +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.theme.components.ButtonSize +import io.element.android.libraries.designsystem.theme.components.TextButton +import io.element.android.libraries.ui.strings.CommonStrings + +@Composable +fun JoinButton( + showProgress: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.textActionAccent) { + TextButton( + modifier = modifier, + text = stringResource(CommonStrings.action_join), + onClick = onClick, + size = ButtonSize.LargeLowPadding, + showProgress = showProgress, + ) + } +} 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 22ef102946..9f41171dd9 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 @@ -31,7 +31,6 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun SpaceHeaderRootView( numberOfSpaces: Int, - numberOfRooms: Int, modifier: Modifier = Modifier, ) { Column( @@ -52,7 +51,7 @@ fun SpaceHeaderRootView( ) SpaceInfoRow( leftText = numberOfSpaces(numberOfSpaces), - rightText = numberOfRooms(numberOfRooms), + rightText = null, ) Text( text = stringResource(CommonStrings.screen_space_list_description), @@ -68,6 +67,5 @@ fun SpaceHeaderRootView( internal fun SpaceHeaderRootViewPreview() = ElementPreview { SpaceHeaderRootView( numberOfSpaces = 3, - numberOfRooms = 10, ) } 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 6dbe7ebf72..e9e151f027 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 @@ -7,6 +7,7 @@ package io.element.android.libraries.matrix.ui.components +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -24,7 +25,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.components.avatar.anAvatarData import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.matrix.api.room.join.JoinRule +import io.element.android.libraries.matrix.api.spaces.SpaceRoomVisibility import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList @@ -38,12 +39,12 @@ fun SpaceHeaderView( avatarData: AvatarData, name: String?, topic: String?, - joinRule: JoinRule?, + visibility: SpaceRoomVisibility, heroes: ImmutableList, numberOfMembers: Int, - numberOfRooms: Int, modifier: Modifier = Modifier, topicMaxLines: Int = Int.MAX_VALUE, + onTopicClick: ((String) -> Unit)? = null, ) { RoomPreviewOrganism( modifier = modifier.padding(24.dp), @@ -64,17 +65,21 @@ fun SpaceHeaderView( } }, subtitle = { - if (joinRule != null) { - SpaceInfoRow( - joinRule = joinRule, - numberOfRooms = numberOfRooms, - ) - } + SpaceInfoRow(visibility = visibility) }, description = if (topic.isNullOrBlank()) { null } else { - { RoomPreviewDescriptionAtom(description = topic, maxLines = topicMaxLines) } + { + RoomPreviewDescriptionAtom( + description = topic, + maxLines = topicMaxLines, + modifier = Modifier.clickable( + enabled = onTopicClick != null, + onClick = { onTopicClick?.invoke(topic) } + ) + ) + } }, memberCount = { SpaceMembersView( @@ -97,7 +102,7 @@ internal fun SpaceHeaderViewPreview() = ElementPreview { name = "Space name", topic = "Space topic: " + LoremIpsum(40).values.first(), topicMaxLines = 2, - joinRule = JoinRule.Public, + visibility = SpaceRoomVisibility.Public, heroes = persistentListOf( aMatrixUser(id = "@1:d", displayName = "Alice", avatarUrl = "aUrl"), aMatrixUser(id = "@2:d", displayName = "Bob"), @@ -105,6 +110,5 @@ internal fun SpaceHeaderViewPreview() = ElementPreview { aMatrixUser(id = "@4:d", displayName = "Dave"), ), numberOfMembers = 999, - numberOfRooms = 10, ) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceInfoRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceInfoRow.kt index 3d973d97ae..d0d14bcdd4 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceInfoRow.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceInfoRow.kt @@ -27,14 +27,16 @@ import io.element.android.compound.tokens.generated.CompoundIcons 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.matrix.api.room.join.JoinRule +import io.element.android.libraries.matrix.api.spaces.SpaceRoomVisibility +import io.element.android.libraries.matrix.ui.model.icon +import io.element.android.libraries.matrix.ui.model.label import io.element.android.libraries.ui.strings.CommonPlurals import io.element.android.libraries.ui.strings.CommonStrings @Composable fun SpaceInfoRow( leftText: String, - rightText: String, + rightText: String?, modifier: Modifier = Modifier, iconVector: ImageVector? = null, ) { @@ -51,7 +53,11 @@ fun SpaceInfoRow( tint = ElementTheme.colors.iconTertiary, ) } - val text = stringResource(id = CommonStrings.screen_space_list_details, leftText, rightText) + val text = if (rightText != null) { + stringResource(id = CommonStrings.screen_space_list_details, leftText, rightText) + } else { + leftText + } Text( text = text, style = ElementTheme.typography.fontBodyLgRegular, @@ -63,34 +69,14 @@ fun SpaceInfoRow( @Composable fun SpaceInfoRow( - joinRule: JoinRule, - numberOfRooms: Int, + visibility: SpaceRoomVisibility, modifier: Modifier = Modifier, ) { - val (leftText, rightText, icon) = when (joinRule) { - JoinRule.Public -> Triple( - stringResource(id = CommonStrings.common_public_space), - numberOfRooms(numberOfRooms), - CompoundIcons.Public(), - ) - // TODO External space - // JoinRule.Private -> Triple( - // stringResource(id = CommonStrings.common_external_space), - // numberOfRooms(numberOfRooms), - // CompoundIcons.Guest(), - // ) - // JoinRule.Private, - else -> Triple( - stringResource(id = CommonStrings.common_private_space), - numberOfRooms(numberOfRooms), - CompoundIcons.Lock(), - ) - } SpaceInfoRow( - leftText = leftText, - rightText = rightText, + leftText = visibility.label, + rightText = null, modifier = modifier, - iconVector = icon, + iconVector = visibility.icon, ) } @@ -124,12 +110,13 @@ internal fun SpaceInfoRowPreview() = ElementPreview { iconVector = CompoundIcons.Workspace(), ) SpaceInfoRow( - joinRule = JoinRule.Private, - numberOfRooms = 4, + visibility = SpaceRoomVisibility.Private, ) SpaceInfoRow( - joinRule = JoinRule.Public, - numberOfRooms = 10, + visibility = SpaceRoomVisibility.Public + ) + SpaceInfoRow( + visibility = SpaceRoomVisibility.Restricted ) } } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomItemView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomItemView.kt index e599b75cab..5a9c49ceb9 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomItemView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomItemView.kt @@ -29,11 +29,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme -import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAtom import io.element.android.libraries.designsystem.atomic.molecules.InviteButtonsRowMolecule import io.element.android.libraries.designsystem.components.avatar.Avatar @@ -41,15 +40,21 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.modifiers.onKeyboardContextMenuAction +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.Text import io.element.android.libraries.designsystem.theme.unreadIndicator import io.element.android.libraries.matrix.api.room.CurrentUserMembership -import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.spaces.SpaceRoom +import io.element.android.libraries.matrix.api.spaces.SpaceRoomVisibility import io.element.android.libraries.matrix.ui.model.getAvatarData +import io.element.android.libraries.matrix.ui.model.icon +import io.element.android.libraries.matrix.ui.model.label import io.element.android.libraries.ui.strings.CommonPlurals import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList @Composable fun SpaceRoomItemView( @@ -59,18 +64,23 @@ fun SpaceRoomItemView( onClick: () -> Unit, onLongClick: () -> Unit, modifier: Modifier = Modifier, + trailingAction: @Composable (() -> Unit)? = null, + bottomAction: @Composable (() -> Unit)? = null, ) { SpaceRoomItemScaffold( modifier = modifier, avatarData = spaceRoom.getAvatarData(AvatarSize.SpaceListItem), isSpace = spaceRoom.isSpace, hideAvatars = hideAvatars, + heroes = spaceRoom.heroes + .map { hero -> hero.getAvatarData(AvatarSize.SpaceListItem) } + .toImmutableList(), onClick = onClick, onLongClick = onLongClick, + trailingAction = trailingAction, ) { NameAndIndicatorRow( - isSpace = spaceRoom.isSpace, - name = spaceRoom.name, + name = spaceRoom.displayName, showIndicator = showUnreadIndicator ) Spacer(modifier = Modifier.height(1.dp)) @@ -79,22 +89,21 @@ fun SpaceRoomItemView( subtitle = spaceRoom.subtitle() ) Spacer(modifier = Modifier.height(1.dp)) - Text( - modifier = Modifier.weight(1f), - style = ElementTheme.typography.fontBodyMdRegular, - text = spaceRoom.info(), - fontStyle = FontStyle.Italic.takeIf { spaceRoom.name == null }, - color = ElementTheme.colors.textSecondary, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - if (spaceRoom.state == CurrentUserMembership.INVITED) { - Spacer(modifier = Modifier.height(12.dp)) - InviteButtonsRowMolecule( - onAcceptClick = {}, - onDeclineClick = {}, + val info = spaceRoom.info() + if (info.isNotBlank()) { + Text( + modifier = Modifier.weight(1f), + style = ElementTheme.typography.fontBodyMdRegular, + text = info, + color = ElementTheme.colors.textSecondary, + maxLines = 1, + overflow = TextOverflow.Ellipsis ) } + if (bottomAction != null) { + Spacer(modifier = Modifier.height(12.dp)) + bottomAction() + } } } @@ -131,8 +140,7 @@ private fun SubtitleRow( @Composable private fun NameAndIndicatorRow( - isSpace: Boolean, - name: String?, + name: String, showIndicator: Boolean, modifier: Modifier = Modifier, ) { @@ -144,8 +152,7 @@ private fun NameAndIndicatorRow( Text( modifier = Modifier.weight(1f), style = ElementTheme.typography.fontBodyLgMedium, - text = name ?: stringResource(id = if (isSpace) CommonStrings.common_no_space_name else CommonStrings.common_no_room_name), - fontStyle = FontStyle.Italic.takeIf { name == null }, + text = name, color = ElementTheme.colors.textPrimary, maxLines = 1, overflow = TextOverflow.Ellipsis @@ -162,11 +169,13 @@ private fun NameAndIndicatorRow( private fun SpaceRoomItemScaffold( avatarData: AvatarData, isSpace: Boolean, + heroes: ImmutableList, onClick: () -> Unit, onLongClick: () -> Unit, hideAvatars: Boolean, modifier: Modifier = Modifier, - content: @Composable ColumnScope.() -> Unit + trailingAction: @Composable (() -> Unit)? = null, + content: @Composable ColumnScope.() -> Unit, ) { val clickModifier = Modifier .combinedClickable( @@ -186,7 +195,7 @@ private fun SpaceRoomItemScaffold( ) { Avatar( avatarData = avatarData, - avatarType = if (isSpace) AvatarType.Space() else AvatarType.Room(), + avatarType = if (isSpace) AvatarType.Space() else AvatarType.Room(heroes = heroes), hideImage = hideAvatars, ) Spacer(modifier = Modifier.width(16.dp)) @@ -194,6 +203,10 @@ private fun SpaceRoomItemScaffold( modifier = Modifier.weight(1f), content = content, ) + if (trailingAction != null) { + Spacer(modifier = Modifier.width(16.dp)) + trailingAction() + } } } @@ -201,11 +214,7 @@ private fun SpaceRoomItemScaffold( @ReadOnlyComposable private fun SpaceRoom.subtitle(): String { return if (isSpace) { - if (joinRule == JoinRule.Public) { - stringResource(CommonStrings.common_public_space) - } else { - stringResource(CommonStrings.common_private_space) - } + visibility.label } else { pluralStringResource(CommonPlurals.common_member_count, numJoinedMembers, numJoinedMembers) } @@ -215,11 +224,7 @@ private fun SpaceRoom.subtitle(): String { @ReadOnlyComposable private fun SpaceRoom.info(): String { return if (isSpace) { - stringResource( - CommonStrings.screen_space_list_details, - pluralStringResource(CommonPlurals.common_rooms, childrenCount, childrenCount), - pluralStringResource(CommonPlurals.common_member_count, numJoinedMembers, numJoinedMembers), - ) + pluralStringResource(CommonPlurals.common_member_count, numJoinedMembers, numJoinedMembers) } else { topic.orEmpty() } @@ -227,9 +232,39 @@ private fun SpaceRoom.info(): String { @Composable private fun SpaceRoom.visibilityIcon(): ImageVector? { - return if (joinRule == JoinRule.Public) { - CompoundIcons.Public() + // Don't show any icon for restricted rooms as it's the default and would add noise + return if (visibility == SpaceRoomVisibility.Restricted) { + null } else { - CompoundIcons.LockSolid() + visibility.icon } } + +@Composable +@PreviewsDayNight +internal fun SpaceRoomItemViewPreview(@PreviewParameter(SpaceRoomProvider::class) spaceRoom: SpaceRoom) = ElementPreview { + SpaceRoomItemView( + spaceRoom = spaceRoom, + showUnreadIndicator = spaceRoom.state == CurrentUserMembership.INVITED, + hideAvatars = false, + onClick = {}, + onLongClick = {}, + modifier = Modifier.fillMaxWidth(), + bottomAction = if (spaceRoom.state == CurrentUserMembership.INVITED) { + { InviteButtonsRowMolecule({}, {}) } + } else { + null + }, + trailingAction = when (spaceRoom.state) { + null, CurrentUserMembership.LEFT -> { + { + JoinButton( + showProgress = spaceRoom.state == CurrentUserMembership.LEFT, + onClick = { }, + ) + } + } + else -> null + } + ) +} diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomProvider.kt new file mode 100644 index 0000000000..4ffda58239 --- /dev/null +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomProvider.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2025 New Vector 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.matrix.ui.components + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.CurrentUserMembership +import io.element.android.libraries.matrix.api.room.RoomType +import io.element.android.libraries.matrix.api.spaces.SpaceRoom +import io.element.android.libraries.previewutils.room.aSpaceRoom + +class SpaceRoomProvider : PreviewParameterProvider { + override val values: Sequence = sequenceOf( + aSpaceRoom( + roomType = RoomType.Room, + displayName = "Room name with topic", + topic = "Room topic that is quite long and might be truncated" + ), + aSpaceRoom( + roomType = RoomType.Room, + displayName = "Room name no topic", + state = CurrentUserMembership.LEFT, + ), + aSpaceRoom( + displayName = "Alice", + roomType = RoomType.Room, + isDirect = true, + heroes = listOf(aMatrixUser(displayName = "Alice")), + state = CurrentUserMembership.JOINED, + numJoinedMembers = 2, + ), + aSpaceRoom( + roomType = RoomType.Room, + displayName = "Room name with topic", + topic = "Room topic that is quite long and might be truncated", + state = CurrentUserMembership.INVITED, + ), + aSpaceRoom( + roomType = RoomType.Room, + displayName = "Room name no topic", + state = CurrentUserMembership.INVITED, + ), + aSpaceRoom( + numJoinedMembers = 5, + childrenCount = 10, + worldReadable = true, + roomId = RoomId("!spaceId0:example.com"), + ), + aSpaceRoom( + numJoinedMembers = 5, + childrenCount = 10, + worldReadable = true, + avatarUrl = "anUrl", + roomId = RoomId("!spaceId1:example.com"), + state = CurrentUserMembership.LEFT, + ), + aSpaceRoom( + numJoinedMembers = 5, + childrenCount = 10, + worldReadable = true, + avatarUrl = "anUrl", + roomId = RoomId("!spaceId2:example.com"), + state = CurrentUserMembership.INVITED, + ), + aSpaceRoom( + displayName = "Alice", + roomType = RoomType.Space, + heroes = listOf(aMatrixUser(displayName = "Alice")), + state = CurrentUserMembership.JOINED, + numJoinedMembers = 2, + ), + ) +} diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataFetcherFactory.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataFetcherFactory.kt index e176c4c03b..c852be62f5 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataFetcherFactory.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataFetcherFactory.kt @@ -11,10 +11,10 @@ import coil3.ImageLoader import coil3.fetch.Fetcher import coil3.request.Options import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.media.MatrixMediaLoader internal class AvatarDataFetcherFactory( - private val client: MatrixClient + private val matrixMediaLoader: MatrixMediaLoader ) : Fetcher.Factory { override fun create( data: AvatarData, @@ -22,7 +22,7 @@ internal class AvatarDataFetcherFactory( imageLoader: ImageLoader ): Fetcher { return CoilMediaFetcher( - mediaLoader = client.mediaLoader, + mediaLoader = matrixMediaLoader, mediaData = data.toMediaRequestData(), ) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt index dc36eb3878..54a1a21e22 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt @@ -18,11 +18,11 @@ import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.Inject import dev.zacsweers.metro.Provider import io.element.android.libraries.di.annotations.ApplicationContext -import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import okhttp3.OkHttpClient interface LoggedInImageLoaderFactory { - fun newImageLoader(matrixClient: MatrixClient): ImageLoader + fun newImageLoader(matrixMediaLoader: MatrixMediaLoader): ImageLoader } @ContributesBinding(AppScope::class) @@ -31,7 +31,7 @@ class DefaultLoggedInImageLoaderFactory( @ApplicationContext private val context: Context, private val okHttpClient: Provider, ) : LoggedInImageLoaderFactory { - override fun newImageLoader(matrixClient: MatrixClient): ImageLoader { + override fun newImageLoader(matrixMediaLoader: MatrixMediaLoader): ImageLoader { return ImageLoader.Builder(context) .components { add( @@ -50,8 +50,8 @@ class DefaultLoggedInImageLoaderFactory( } add(AvatarDataKeyer()) add(MediaRequestDataKeyer()) - add(AvatarDataFetcherFactory(matrixClient)) - add(MediaRequestDataFetcherFactory(matrixClient)) + add(AvatarDataFetcherFactory(matrixMediaLoader)) + add(MediaRequestDataFetcherFactory(matrixMediaLoader)) } .build() } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt index 82080c66cc..6720eaae0f 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt @@ -49,7 +49,7 @@ class DefaultImageLoaderHolder( return synchronized(map) { map.getOrPut(client.sessionId) { loggedInImageLoaderFactory - .newImageLoader(client) + .newImageLoader(client.matrixMediaLoader) } } } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataFetcherFactory.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataFetcherFactory.kt index 2ac2ebe511..bc6cb7663c 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataFetcherFactory.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataFetcherFactory.kt @@ -10,10 +10,10 @@ package io.element.android.libraries.matrix.ui.media import coil3.ImageLoader import coil3.fetch.Fetcher import coil3.request.Options -import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.media.MatrixMediaLoader internal class MediaRequestDataFetcherFactory( - private val client: MatrixClient + private val matrixMediaLoader: MatrixMediaLoader, ) : Fetcher.Factory { override fun create( data: MediaRequestData, @@ -21,7 +21,7 @@ internal class MediaRequestDataFetcherFactory( imageLoader: ImageLoader ): Fetcher { return CoilMediaFetcher( - mediaLoader = client.mediaLoader, + mediaLoader = matrixMediaLoader, mediaData = data, ) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/SpaceExtension.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/SpaceExtension.kt index e4b056fdea..1c3111a2a2 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/SpaceExtension.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/SpaceExtension.kt @@ -7,13 +7,41 @@ package io.element.android.libraries.matrix.ui.model +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import io.element.android.compound.tokens.generated.CompoundIcons 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.spaces.SpaceRoom +import io.element.android.libraries.matrix.api.spaces.SpaceRoomVisibility +import io.element.android.libraries.ui.strings.CommonStrings fun SpaceRoom.getAvatarData(size: AvatarSize) = AvatarData( id = roomId.value, - name = name, + name = displayName, url = avatarUrl, size = size, ) + +val SpaceRoomVisibility.icon: ImageVector + @Composable + get() { + return when (this) { + SpaceRoomVisibility.Private -> CompoundIcons.LockSolid() + SpaceRoomVisibility.Public -> CompoundIcons.Public() + SpaceRoomVisibility.Restricted -> CompoundIcons.Workspace() + } + } + +val SpaceRoomVisibility.label: String + @Composable + @ReadOnlyComposable + get() { + return when (this) { + SpaceRoomVisibility.Private -> stringResource(CommonStrings.common_private_space) + SpaceRoomVisibility.Public -> stringResource(CommonStrings.common_public_space) + SpaceRoomVisibility.Restricted -> stringResource(CommonStrings.common_shared_space) + } + } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/ObserveRoomMemberIdentityStateChange.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/ObserveRoomMemberIdentityStateChange.kt index 0001131e59..0e46463807 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/ObserveRoomMemberIdentityStateChange.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/ObserveRoomMemberIdentityStateChange.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.matrix.ui.model.getAvatarData import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -46,7 +46,7 @@ fun JoinedRoom.roomMemberIdentityStateChange(waitForEncryption: Boolean): Flow { return remember { - mediaPreviewService() + mediaPreviewService .mediaPreviewConfigFlow .mapState { config -> config.hideInviteAvatar } }.collectAsState() diff --git a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/media/DefaultImageLoaderHolderTest.kt b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/media/DefaultImageLoaderHolderTest.kt index 5b3bc33d9f..a30305176b 100644 --- a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/media/DefaultImageLoaderHolderTest.kt +++ b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/media/DefaultImageLoaderHolderTest.kt @@ -10,7 +10,7 @@ package io.element.android.libraries.matrix.ui.media import androidx.test.platform.app.InstrumentationRegistry import coil3.ImageLoader import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.sessionstorage.test.observer.FakeSessionObserver @@ -27,7 +27,7 @@ class DefaultImageLoaderHolderTest { @Test fun `get - returns the same ImageLoader for the same client`() { val context = InstrumentationRegistry.getInstrumentation().context - val lambda = lambdaRecorder { ImageLoader.Builder(context).build() } + val lambda = lambdaRecorder { ImageLoader.Builder(context).build() } val holder = DefaultImageLoaderHolder( loggedInImageLoaderFactory = FakeLoggedInImageLoaderFactory(lambda), @@ -39,14 +39,14 @@ class DefaultImageLoaderHolderTest { assert(imageLoader1 === imageLoader2) lambda.assertions() .isCalledOnce() - .with(value(client)) + .with(value(client.matrixMediaLoader)) } @Test fun `when session is deleted, the image loader is deleted`() = runTest { val context = InstrumentationRegistry.getInstrumentation().context val lambda = - lambdaRecorder { ImageLoader.Builder(context).build() } + lambdaRecorder { ImageLoader.Builder(context).build() } val sessionObserver = FakeSessionObserver() val holder = DefaultImageLoaderHolder( loggedInImageLoaderFactory = FakeLoggedInImageLoaderFactory(lambda), @@ -65,7 +65,7 @@ class DefaultImageLoaderHolderTest { fun `when session is created, nothing happen`() = runTest { val context = InstrumentationRegistry.getInstrumentation().context val lambda = - lambdaRecorder { ImageLoader.Builder(context).build() } + lambdaRecorder { ImageLoader.Builder(context).build() } val sessionObserver = FakeSessionObserver() DefaultImageLoaderHolder( loggedInImageLoaderFactory = FakeLoggedInImageLoaderFactory(lambda), diff --git a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/media/FakeLoggedInImageLoaderFactory.kt b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/media/FakeLoggedInImageLoaderFactory.kt index a4f5bbdfb7..56ee9ee653 100644 --- a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/media/FakeLoggedInImageLoaderFactory.kt +++ b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/media/FakeLoggedInImageLoaderFactory.kt @@ -8,12 +8,12 @@ package io.element.android.libraries.matrix.ui.media import coil3.ImageLoader -import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.media.MatrixMediaLoader class FakeLoggedInImageLoaderFactory( - private val newImageLoaderLambda: (MatrixClient) -> ImageLoader + private val newImageLoaderLambda: (MatrixMediaLoader) -> ImageLoader ) : LoggedInImageLoaderFactory { - override fun newImageLoader(matrixClient: MatrixClient): ImageLoader { - return newImageLoaderLambda(matrixClient) + override fun newImageLoader(matrixMediaLoader: MatrixMediaLoader): ImageLoader { + return newImageLoaderLambda(matrixMediaLoader) } } diff --git a/libraries/mediapickers/api/build.gradle.kts b/libraries/mediapickers/api/build.gradle.kts index 6d24930ac1..c130cd7900 100644 --- a/libraries/mediapickers/api/build.gradle.kts +++ b/libraries/mediapickers/api/build.gradle.kts @@ -18,7 +18,6 @@ android { implementation(projects.libraries.uiStrings) implementation(projects.libraries.core) implementation(projects.libraries.di) - implementation(libs.inject) testCommonDependencies(libs) } diff --git a/libraries/mediapickers/impl/build.gradle.kts b/libraries/mediapickers/impl/build.gradle.kts index 7ca588844d..6a90b95306 100644 --- a/libraries/mediapickers/impl/build.gradle.kts +++ b/libraries/mediapickers/impl/build.gradle.kts @@ -20,6 +20,5 @@ android { dependencies { implementation(projects.libraries.core) implementation(projects.libraries.di) - implementation(libs.inject) api(projects.libraries.mediapickers.api) } diff --git a/libraries/mediapickers/test/build.gradle.kts b/libraries/mediapickers/test/build.gradle.kts index 17517d886d..ee743bb63d 100644 --- a/libraries/mediapickers/test/build.gradle.kts +++ b/libraries/mediapickers/test/build.gradle.kts @@ -19,7 +19,6 @@ android { dependencies { implementation(projects.libraries.core) implementation(projects.libraries.di) - implementation(libs.inject) api(projects.libraries.mediapickers.api) } } diff --git a/libraries/mediaupload/api/build.gradle.kts b/libraries/mediaupload/api/build.gradle.kts index 1219eb6337..bea5398372 100644 --- a/libraries/mediaupload/api/build.gradle.kts +++ b/libraries/mediaupload/api/build.gradle.kts @@ -25,7 +25,6 @@ dependencies { implementation(projects.libraries.di) api(projects.libraries.matrix.api) api(projects.libraries.preferences.api) - implementation(libs.inject) implementation(libs.coroutines.core) testCommonDependencies(libs) diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt index 62731d1117..7592f46fa6 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt @@ -10,7 +10,7 @@ package io.element.android.libraries.mediaupload.api import android.net.Uri import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.androidutils.hash.hash import io.element.android.libraries.core.extensions.flatMap import io.element.android.libraries.core.extensions.flatMapCatching @@ -25,7 +25,7 @@ import timber.log.Timber import java.io.File import java.util.concurrent.ConcurrentHashMap -@Inject +@AssistedInject class MediaSender( private val preProcessor: MediaPreProcessor, private val room: JoinedRoom, diff --git a/libraries/mediaupload/impl/build.gradle.kts b/libraries/mediaupload/impl/build.gradle.kts index 64a9e47bdf..ffd01aac2f 100644 --- a/libraries/mediaupload/impl/build.gradle.kts +++ b/libraries/mediaupload/impl/build.gradle.kts @@ -32,7 +32,6 @@ dependencies { implementation(projects.libraries.di) implementation(projects.libraries.matrix.api) implementation(projects.services.toolbox.api) - implementation(libs.inject) implementation(libs.androidx.exifinterface) implementation(libs.androidx.media3.transformer) implementation(libs.androidx.media3.effect) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/TimelineMediaItemsFactory.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/TimelineMediaItemsFactory.kt index f4c8a83a94..a9ee0a89e2 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/TimelineMediaItemsFactory.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/TimelineMediaItemsFactory.kt @@ -15,7 +15,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.mediaviewer.impl.model.MediaItem import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.distinctUntilChanged @@ -69,7 +69,7 @@ class TimelineMediaItemsFactory( newTimelineItemStates.add(cacheItem) } } - _timelineItems.emit(newTimelineItemStates.toPersistentList()) + _timelineItems.emit(newTimelineItemStates.toImmutableList()) } private fun buildAndCacheItem( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryNode.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryNode.kt index 01530aae18..06a3c6a58f 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryNode.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryNode.kt @@ -15,7 +15,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId @@ -24,7 +24,7 @@ import io.element.android.libraries.mediaviewer.impl.gallery.di.MediaItemPresent import io.element.android.libraries.mediaviewer.impl.model.MediaItem @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class MediaGalleryNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt index f8addd5463..ba3d4d5e1f 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt @@ -18,7 +18,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.androidutils.R import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter @@ -44,7 +44,7 @@ import io.element.android.libraries.mediaviewer.impl.model.mediaSource import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.launch -@Inject +@AssistedInject class MediaGalleryPresenter( @Assisted private val navigator: MediaGalleryNavigator, private val room: BaseRoom, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryFlowNode.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryFlowNode.kt index 4f899ac0f5..e617829ad0 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryFlowNode.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryFlowNode.kt @@ -16,7 +16,7 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.BackstackWithOverlayBox import io.element.android.libraries.architecture.BaseFlowNode @@ -40,7 +40,7 @@ import io.element.android.libraries.mediaviewer.impl.model.thumbnailSource import kotlinx.parcelize.Parcelize @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class MediaGalleryFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt index 23ccb9b68b..6d5fdd9612 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt @@ -53,7 +53,7 @@ import io.element.android.libraries.voiceplayer.api.VoiceMessageEvents import io.element.android.libraries.voiceplayer.api.VoiceMessageState import io.element.android.libraries.voiceplayer.api.VoiceMessageStateProvider import io.element.android.libraries.voiceplayer.api.aVoiceMessageState -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.delay @Composable @@ -133,7 +133,7 @@ private fun VoiceInfoRow( .height(34.dp), showCursor = state.showCursor, playbackProgress = state.progress, - waveform = voice.mediaInfo.waveform.orEmpty().toPersistentList(), + waveform = voice.mediaInfo.waveform.orEmpty().toImmutableList(), onSeek = { state.eventSink(VoiceMessageEvents.Seek(it)) }, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/voice/VoiceMessagePresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/voice/VoiceMessagePresenter.kt index ab133caaf7..830a405110 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/voice/VoiceMessagePresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/voice/VoiceMessagePresenter.kt @@ -10,10 +10,10 @@ package io.element.android.libraries.mediaviewer.impl.gallery.voice import androidx.compose.runtime.Composable import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.BindingContainer import dev.zacsweers.metro.Binds import dev.zacsweers.metro.ContributesTo -import dev.zacsweers.metro.Inject import dev.zacsweers.metro.IntoMap import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.di.RoomScope @@ -33,7 +33,7 @@ interface VoiceMessagePresenterModule { fun bindVoiceMessagePresenterFactory(factory: VoiceMessagePresenter.Factory): MediaItemPresenterFactory<*, *> } -@Inject +@AssistedInject class VoiceMessagePresenter( voiceMessagePresenterFactory: VoiceMessagePresenterFactory, @Assisted private val item: MediaItem.Voice, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaAudioView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaAudioView.kt index 729e2c1605..54a6127d9d 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaAudioView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaAudioView.kt @@ -71,7 +71,7 @@ import io.element.android.libraries.mediaviewer.impl.local.player.rememberExoPla import io.element.android.libraries.mediaviewer.impl.local.player.seekToEnsurePlaying import io.element.android.libraries.mediaviewer.impl.local.player.togglePlay import io.element.android.libraries.mediaviewer.impl.local.rememberLocalMediaViewState -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.delay @SuppressLint("UnsafeOptInUsageError") @@ -252,7 +252,7 @@ private fun ExoPlayerMediaAudioView( .height(48.dp), playbackProgress = mediaPlayerControllerState.progressAsFloat, showCursor = true, - waveform = waveform.toPersistentList(), + waveform = waveform.toImmutableList(), onSeek = { exoPlayer.seekToEnsurePlaying((it * exoPlayer.duration).toLong()) }, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt index b2eb7e7b6f..1046a80fe3 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt @@ -30,9 +30,9 @@ import io.element.android.libraries.mediaviewer.impl.model.mediaInfo import io.element.android.libraries.mediaviewer.impl.model.mediaSource import io.element.android.libraries.mediaviewer.impl.model.thumbnailSource import io.element.android.services.toolbox.api.systemclock.SystemClock -import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -72,12 +72,12 @@ class MediaViewerDataSource( } @Composable - fun collectAsState(): State> { + fun collectAsState(): State> { return remember { dataFlow() }.collectAsState(initialData()) } @VisibleForTesting - internal fun dataFlow(): Flow> { + internal fun dataFlow(): Flow> { return galleryDataSource.groupedMediaItemsFlow() .map { groupedItems -> when (groupedItems) { @@ -106,7 +106,7 @@ class MediaViewerDataSource( } } - private fun initialData(): PersistentList { + private fun initialData(): ImmutableList { val initialMediaItems = galleryDataSource.getLastData().dataOrNull()?.getItems(galleryMode).orEmpty() return buildMediaViewerPageList(initialMediaItems) @@ -149,7 +149,7 @@ class MediaViewerDataSource( ) } } - }.toPersistentList() + }.toImmutableList() fun clearLoadingError(data: MediaViewerPageData.MediaViewerData) { localMediaStates[data.mediaSource.url]?.value = AsyncData.Uninitialized diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNode.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNode.kt index 7165ac7c8e..cb9743bf97 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNode.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNode.kt @@ -14,7 +14,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.compound.theme.ForcedDarkElementTheme import io.element.android.features.viewfolder.api.TextFileViewer @@ -33,7 +33,7 @@ import io.element.android.libraries.mediaviewer.impl.model.hasEvent import io.element.android.services.toolbox.api.systemclock.SystemClock @ContributesNode(RoomScope::class) -@Inject +@AssistedInject class MediaViewerNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt index 93ef09cf4f..c7b93a227f 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt @@ -23,7 +23,7 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher @@ -40,7 +40,7 @@ import io.element.android.libraries.mediaviewer.impl.R import io.element.android.libraries.mediaviewer.impl.details.MediaBottomSheetState import io.element.android.libraries.mediaviewer.impl.local.LocalMediaActions import io.element.android.libraries.ui.strings.CommonStrings -import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter @@ -49,7 +49,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import io.element.android.libraries.androidutils.R as UtilsR -@Inject +@AssistedInject class MediaViewerPresenter( @Assisted private val inputs: MediaViewerEntryPoint.Params, @Assisted private val navigator: MediaViewerNavigator, @@ -162,7 +162,7 @@ class MediaViewerPresenter( @Composable private fun NoMoreItemsBackwardSnackBarDisplayer( currentIndex: IntState, - data: State>, + data: State>, ) { val isRenderingLoadingBackward by remember { derivedStateOf { @@ -186,7 +186,7 @@ class MediaViewerPresenter( @Composable private fun NoMoreItemsForwardSnackBarDisplayer( currentIndex: IntState, - data: State>, + data: State>, ) { val isRenderingLoadingForward by remember { derivedStateOf { diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt index 324b093325..05a55b4e5f 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt @@ -26,7 +26,7 @@ import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.mediaviewer.impl.details.MediaBottomSheetState import io.element.android.libraries.mediaviewer.impl.details.aMediaDeleteConfirmationState import io.element.android.libraries.mediaviewer.impl.details.aMediaDetailsBottomSheetState -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList open class MediaViewerStateProvider : PreviewParameterProvider { override val values: Sequence @@ -204,7 +204,7 @@ fun aMediaViewerState( eventSink: (MediaViewerEvents) -> Unit = {}, ) = MediaViewerState( initiallySelectedEventId = EventId("\$a:b"), - listData = listData.toPersistentList(), + listData = listData.toImmutableList(), currentIndex = currentIndex, snackbarMessage = null, canShowInfo = canShowInfo, diff --git a/libraries/mediaviewer/impl/src/main/res/values-bg/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-bg/translations.xml index e6874e6743..8607121680 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-bg/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-bg/translations.xml @@ -1,5 +1,6 @@ + "Зареждане на медийни файлове…" "Файлове" "Медия" "Медия и файлове" diff --git a/libraries/oidc/api/src/main/kotlin/io/element/android/libraries/oidc/api/OidcAction.kt b/libraries/oidc/api/src/main/kotlin/io/element/android/libraries/oidc/api/OidcAction.kt index abd83d098f..fc464e9ee2 100644 --- a/libraries/oidc/api/src/main/kotlin/io/element/android/libraries/oidc/api/OidcAction.kt +++ b/libraries/oidc/api/src/main/kotlin/io/element/android/libraries/oidc/api/OidcAction.kt @@ -8,6 +8,6 @@ package io.element.android.libraries.oidc.api sealed interface OidcAction { - data object GoBack : OidcAction + data class GoBack(val toUnblock: Boolean = false) : OidcAction data class Success(val url: String) : OidcAction } diff --git a/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/OidcUrlParser.kt b/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/OidcUrlParser.kt index 05257d2b0a..1e9b6953a8 100644 --- a/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/OidcUrlParser.kt +++ b/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/OidcUrlParser.kt @@ -36,7 +36,7 @@ class DefaultOidcUrlParser( */ override fun parse(url: String): OidcAction? { if (url.startsWith(oidcRedirectUrlProvider.provide()).not()) return null - if (url.contains("error=access_denied")) return OidcAction.GoBack + if (url.contains("error=access_denied")) return OidcAction.GoBack() if (url.contains("code=")) return OidcAction.Success(url) // Other case not supported, let's crash the app for now diff --git a/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcActionFlowTest.kt b/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcActionFlowTest.kt index 3b56f28c5a..51017f0af0 100644 --- a/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcActionFlowTest.kt +++ b/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcActionFlowTest.kt @@ -24,10 +24,10 @@ class DefaultOidcActionFlowTest { data.add(action) } } - sut.post(OidcAction.GoBack) + sut.post(OidcAction.GoBack()) delay(1) sut.reset() delay(1) - assertThat(data).containsExactly(OidcAction.GoBack, null) + assertThat(data).containsExactly(OidcAction.GoBack(), null) } } diff --git a/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcIntentResolverTest.kt b/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcIntentResolverTest.kt index e48e0c2e1e..48595452d2 100644 --- a/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcIntentResolverTest.kt +++ b/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcIntentResolverTest.kt @@ -29,7 +29,7 @@ class DefaultOidcIntentResolverTest { data = "io.element.android:/?error=access_denied&state=IFF1UETGye2ZA8pO".toUri() } val result = sut.resolve(intent) - assertThat(result).isEqualTo(OidcAction.GoBack) + assertThat(result).isEqualTo(OidcAction.GoBack()) } @Test diff --git a/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcUrlParserTest.kt b/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcUrlParserTest.kt index e40424ca0e..7ec03a258e 100644 --- a/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcUrlParserTest.kt +++ b/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcUrlParserTest.kt @@ -31,7 +31,7 @@ class DefaultOidcUrlParserTest { fun `test cancel url`() { val sut = createDefaultOidcUrlParser() val aCancelUrl = "$FAKE_REDIRECT_URL?error=access_denied&state=IFF1UETGye2ZA8pO" - assertThat(sut.parse(aCancelUrl)).isEqualTo(OidcAction.GoBack) + assertThat(sut.parse(aCancelUrl)).isEqualTo(OidcAction.GoBack()) } @Test diff --git a/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt index d7e30579ec..f12d92a50d 100644 --- a/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt +++ b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt @@ -24,8 +24,8 @@ import com.google.accompanist.permissions.shouldShowRationale import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.ContributesBinding -import dev.zacsweers.metro.Inject import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.permissions.api.PermissionsEvents import io.element.android.libraries.permissions.api.PermissionsPresenter @@ -37,7 +37,7 @@ import timber.log.Timber private val loggerTag = LoggerTag("DefaultPermissionsPresenter") -@Inject +@AssistedInject class DefaultPermissionsPresenter( @Assisted val permission: String, private val permissionsStore: PermissionsStore, diff --git a/libraries/permissions/impl/src/main/res/values-bg/translations.xml b/libraries/permissions/impl/src/main/res/values-bg/translations.xml new file mode 100644 index 0000000000..7c8a783a18 --- /dev/null +++ b/libraries/permissions/impl/src/main/res/values-bg/translations.xml @@ -0,0 +1,5 @@ + + + "Проверка дали приложението може да показва известия." + "Проверка на разрешенията" + diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/SessionPreferencesModule.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/SessionPreferencesModule.kt index d9621cd6e0..225ea4e3d2 100644 --- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/SessionPreferencesModule.kt +++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/SessionPreferencesModule.kt @@ -12,7 +12,7 @@ import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.Provides import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.annotations.SessionCoroutineScope -import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.preferences.api.store.SessionPreferencesStore import kotlinx.coroutines.CoroutineScope @@ -22,10 +22,10 @@ object SessionPreferencesModule { @Provides fun providesSessionPreferencesStore( defaultSessionPreferencesStoreFactory: DefaultSessionPreferencesStoreFactory, - currentSessionIdHolder: CurrentSessionIdHolder, + sessionId: SessionId, @SessionCoroutineScope sessionCoroutineScope: CoroutineScope, ): SessionPreferencesStore { return defaultSessionPreferencesStoreFactory - .get(currentSessionIdHolder.current, sessionCoroutineScope) + .get(sessionId, sessionCoroutineScope) } } diff --git a/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/SpaceRoomFixture.kt b/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/SpaceRoomFixture.kt index 3acea6255b..aae6bbfb76 100644 --- a/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/SpaceRoomFixture.kt +++ b/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/SpaceRoomFixture.kt @@ -14,9 +14,11 @@ import io.element.android.libraries.matrix.api.room.RoomType import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.spaces.SpaceRoom import io.element.android.libraries.matrix.api.user.MatrixUser +import kotlinx.collections.immutable.toImmutableList fun aSpaceRoom( - name: String? = "Space name", + rawName: String? = null, + displayName: String = "Space name", avatarUrl: String? = null, canonicalAlias: RoomAlias? = null, childrenCount: Int = 0, @@ -29,18 +31,23 @@ fun aSpaceRoom( state: CurrentUserMembership? = null, topic: String? = null, worldReadable: Boolean = false, + isDirect: Boolean? = null, + via: List = emptyList(), ) = SpaceRoom( - name = name, + rawName = rawName, + displayName = displayName, avatarUrl = avatarUrl, canonicalAlias = canonicalAlias, childrenCount = childrenCount, guestCanJoin = guestCanJoin, - heroes = heroes, + heroes = heroes.toImmutableList(), joinRule = joinRule, numJoinedMembers = numJoinedMembers, roomId = roomId, roomType = roomType, state = state, topic = topic, - worldReadable = worldReadable + worldReadable = worldReadable, + via = via.toImmutableList(), + isDirect = isDirect ) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriber.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriber.kt index 7b7a6a33e8..7dbe4e795e 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriber.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriber.kt @@ -50,7 +50,7 @@ class DefaultPusherSubscriber( Timber.tag(loggerTag.value) .d("Unnecessary to register again the same pusher, but do it in case the pusher has been removed from the server") } - return matrixClient.pushersService() + return matrixClient.pushersService .setHttpPusher( createHttpPusher(pushKey, gateway, matrixClient.sessionId) ) @@ -100,7 +100,7 @@ class DefaultPusherSubscriber( gateway: String, ): Result { val userDataStore = userPushStoreFactory.getOrCreate(matrixClient.sessionId) - return matrixClient.pushersService() + return matrixClient.pushersService .unsetHttpPusher( unsetHttpPusherData = UnsetHttpPusherData( pushKey = pushKey, 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 38a1d4f753..868c7e767a 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 @@ -98,7 +98,7 @@ class DefaultNotifiableEventResolver( val ids = notificationEventRequests.groupBy { it.roomId }.mapValues { (_, value) -> value.map { it.eventId } } // TODO this notificationData is not always valid at the moment, sometimes the Rust SDK can't fetch the matching event - val notificationsResult = client.notificationService().getNotifications(ids) + val notificationsResult = client.notificationService.getNotifications(ids) if (notificationsResult.isFailure) { val exception = notificationsResult.exceptionOrNull() @@ -131,7 +131,7 @@ class DefaultNotifiableEventResolver( ): Result = runCatchingExceptions { when (val content = this.content) { is NotificationContent.MessageLike.RoomMessage -> { - val showMediaPreview = client.mediaPreviewService().getMediaPreviewValue() == MediaPreviewValue.On + val showMediaPreview = client.mediaPreviewService.getMediaPreviewValue() == MediaPreviewValue.On val senderDisambiguatedDisplayName = getDisambiguatedDisplayName(content.senderId) val messageBody = descriptionFromMessageContent(content, senderDisambiguatedDisplayName) val notifiableMessageEvent = buildNotifiableMessageEvent( diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultOnMissedCallNotificationHandler.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultOnMissedCallNotificationHandler.kt index dbabb33058..084ad8d832 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultOnMissedCallNotificationHandler.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultOnMissedCallNotificationHandler.kt @@ -30,7 +30,7 @@ class DefaultOnMissedCallNotificationHandler( ) { // Resolve the event and add a notification for it, at this point it should no longer be a ringing one val notificationData = matrixClientProvider.getOrRestore(sessionId).getOrNull() - ?.notificationService() + ?.notificationService ?.getNotifications(mapOf(roomId to listOf(eventId))) ?.getOrNull() ?.get(eventId) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt index bf3aacd76c..2bbc7208be 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt @@ -10,8 +10,8 @@ package io.element.android.libraries.push.impl.notifications import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.ContributesBinding -import dev.zacsweers.metro.Inject import io.element.android.libraries.core.extensions.mapCatchingExceptions import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.matrix.api.MatrixClient @@ -58,7 +58,7 @@ interface NotificationMediaRepo { ): Result } -@Inject +@AssistedInject class DefaultNotificationMediaRepo( @CacheDirectory private val cacheDir: File, private val mxcTools: MxcTools, @@ -72,7 +72,7 @@ class DefaultNotificationMediaRepo( ): DefaultNotificationMediaRepo } - private val matrixMediaLoader = client.mediaLoader + private val matrixMediaLoader = client.matrixMediaLoader override suspend fun getMediaFile( mediaSource: MediaSource, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt index da5e0dc785..f5f0ce5cca 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt @@ -7,8 +7,12 @@ package io.element.android.libraries.push.impl.notifications.channels +import android.content.ContentResolver +import android.content.Context import android.media.AudioAttributes +import android.media.AudioAttributes.USAGE_NOTIFICATION import android.media.AudioManager +import android.net.Uri import android.os.Build import android.provider.Settings import androidx.annotation.ChecksSdkIntAtLeast @@ -19,6 +23,7 @@ import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.Inject import dev.zacsweers.metro.SingleIn import io.element.android.appconfig.NotificationConfig +import io.element.android.libraries.di.annotations.ApplicationContext import io.element.android.libraries.push.impl.R import io.element.android.services.toolbox.api.strings.StringProvider @@ -26,7 +31,7 @@ import io.element.android.services.toolbox.api.strings.StringProvider * IDs for channels * ========================================================================================== */ internal const val SILENT_NOTIFICATION_CHANNEL_ID = "DEFAULT_SILENT_NOTIFICATION_CHANNEL_ID_V2" -internal const val NOISY_NOTIFICATION_CHANNEL_ID = "DEFAULT_NOISY_NOTIFICATION_CHANNEL_ID" +internal const val NOISY_NOTIFICATION_CHANNEL_ID = "DEFAULT_NOISY_NOTIFICATION_CHANNEL_ID_V2" internal const val CALL_NOTIFICATION_CHANNEL_ID = "CALL_NOTIFICATION_CHANNEL_ID_V3" internal const val RINGING_CALL_NOTIFICATION_CHANNEL_ID = "RINGING_CALL_NOTIFICATION_CHANNEL_ID" @@ -61,6 +66,8 @@ private fun supportNotificationChannels() = Build.VERSION.SDK_INT >= Build.VERSI class DefaultNotificationChannels( private val notificationManager: NotificationManagerCompat, private val stringProvider: StringProvider, + @ApplicationContext + private val context: Context, ) : NotificationChannels { init { createNotificationChannels() @@ -94,6 +101,7 @@ class DefaultNotificationChannels( // Migration - Remove deprecated channels for (channelId in listOf( "DEFAULT_SILENT_NOTIFICATION_CHANNEL_ID", + "DEFAULT_NOISY_NOTIFICATION_CHANNEL_ID", "CALL_NOTIFICATION_CHANNEL_ID", "CALL_NOTIFICATION_CHANNEL_ID_V2", "LISTEN_FOR_EVENTS_NOTIFICATION_CHANNEL_ID", @@ -112,6 +120,17 @@ class DefaultNotificationChannels( NOISY_NOTIFICATION_CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_DEFAULT ) + .setSound( + Uri.Builder() + .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) + // Strangely wwe have to provide a "//" before the package name + .path("//" + context.packageName + "/" + R.raw.message) + .build(), + AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(USAGE_NOTIFICATION) + .build(), + ) .setName(stringProvider.getString(R.string.notification_channel_noisy).ifEmpty { "Noisy notifications" }) .setDescription(stringProvider.getString(R.string.notification_channel_noisy)) .setVibrationEnabled(true) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/CurrentPushProviderTest.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/CurrentPushProviderTest.kt index 5033274998..3eeaae1b2d 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/CurrentPushProviderTest.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/CurrentPushProviderTest.kt @@ -7,10 +7,11 @@ package io.element.android.libraries.push.impl.troubleshoot -import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesIntoSet import dev.zacsweers.metro.Inject -import io.element.android.libraries.push.api.GetCurrentPushProvider +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.push.api.PushService import io.element.android.libraries.push.impl.R import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestDelegate @@ -19,10 +20,11 @@ import io.element.android.services.toolbox.api.strings.StringProvider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.StateFlow -@ContributesIntoSet(AppScope::class) +@ContributesIntoSet(SessionScope::class) @Inject class CurrentPushProviderTest( - private val getCurrentPushProvider: GetCurrentPushProvider, + private val pushService: PushService, + private val sessionId: SessionId, private val stringProvider: StringProvider, ) : NotificationTroubleshootTest { override val order = 110 @@ -35,17 +37,55 @@ class CurrentPushProviderTest( override suspend fun run(coroutineScope: CoroutineScope) { delegate.start() - val provider = getCurrentPushProvider.getCurrentPushProvider() - if (provider != null) { - delegate.updateState( - description = stringProvider.getString(R.string.troubleshoot_notifications_test_current_push_provider_success, provider), - status = NotificationTroubleshootTestState.Status.Success - ) - } else { + val pushProvider = pushService.getCurrentPushProvider() + if (pushProvider == null) { delegate.updateState( description = stringProvider.getString(R.string.troubleshoot_notifications_test_current_push_provider_failure), status = NotificationTroubleshootTestState.Status.Failure() ) + } else if (pushProvider.supportMultipleDistributors.not()) { + delegate.updateState( + description = stringProvider.getString( + R.string.troubleshoot_notifications_test_current_push_provider_success, + pushProvider.name + ), + status = NotificationTroubleshootTestState.Status.Success + ) + } else { + val distributorValue = pushProvider.getCurrentDistributorValue(sessionId) + if (distributorValue == null) { + // No distributors configured + delegate.updateState( + description = stringProvider.getString( + R.string.troubleshoot_notifications_test_current_push_provider_failure_no_distributor, + pushProvider.name + ), + status = NotificationTroubleshootTestState.Status.Failure(false) + ) + } else { + val distributor = pushProvider.getDistributors().find { it.value == distributorValue } + if (distributor == null) { + // Distributor has been uninstalled? + delegate.updateState( + description = stringProvider.getString( + R.string.troubleshoot_notifications_test_current_push_provider_failure_distributor_not_found, + pushProvider.name, + distributorValue, + distributorValue, + ), + status = NotificationTroubleshootTestState.Status.Failure(false) + ) + } else { + delegate.updateState( + description = stringProvider.getString( + R.string.troubleshoot_notifications_test_current_push_provider_success_with_distributor, + pushProvider.name, + distributorValue, + ), + status = NotificationTroubleshootTestState.Status.Success + ) + } + } } } diff --git a/libraries/push/impl/src/main/res/raw/message.mp3 b/libraries/push/impl/src/main/res/raw/message.mp3 new file mode 100644 index 0000000000..5e9645adaf Binary files /dev/null and b/libraries/push/impl/src/main/res/raw/message.mp3 differ diff --git a/libraries/push/impl/src/main/res/values-bg/translations.xml b/libraries/push/impl/src/main/res/values-bg/translations.xml index ca1d5c08c8..3de0fd470b 100644 --- a/libraries/push/impl/src/main/res/values-bg/translations.xml +++ b/libraries/push/impl/src/main/res/values-bg/translations.xml @@ -1,5 +1,7 @@ + "Обаждане" + "Слушане за събития" "Шумни известия" "Безшумни известия" @@ -10,12 +12,14 @@ "%d известие" "%d известия" + "Имате нови съобщения." "** Неуспешно изпращане - моля, отворете стаята" "Присъединяване" "%d покана" "%d покани" + "Поканиха ви за чат" "Ви спомена: %1$s" "Нови съобщения" @@ -26,8 +30,14 @@ "Отбелязване като прочетено" "Бърз отговор" "Ви покани да се присъедините към стаята" + "Аз" + "Преглеждате известието! Кликнете върху мен!" "%1$s: %2$s" "%1$s: %2$s %3$s" + + "%d непрочетено известено съобщение" + "%d непрочетени известени съобщения" + "%1$s и %2$s" "%1$s в %2$s" "%1$s в %2$s и %3$s" @@ -35,5 +45,20 @@ "%d стая" "%d стаи" + "Синхронизация на заден план" + "Услуги на Google" + "Не са намерени валидни услуги на Google Play. Известията може да не работят правилно." + "Проверка на блокирани потребители" + "Преглед на блокираните потребители" + "Няма блокирани потребители." + "Блокирани потребители" + "Получаване на името на текущия доставчик." + "Приложението е изградено с поддръжка за: %1$s" + "Проверка дали приложението може да показва известия." + "Известието не е било кликнато." + "Не може да се покаже известието." + "Известието беше натиснато!" + "Показване на известие" + "Моля, натиснете известието, за да продължите теста." "Грешка: %1$s" diff --git a/libraries/push/impl/src/main/res/values-cs/translations.xml b/libraries/push/impl/src/main/res/values-cs/translations.xml index d072a1c3b1..002c6d18ac 100644 --- a/libraries/push/impl/src/main/res/values-cs/translations.xml +++ b/libraries/push/impl/src/main/res/values-cs/translations.xml @@ -60,6 +60,15 @@ "Synchronizace na pozadí" "Služby Google" "Nebyly nalezeny žádné funkční služby Google Play. Oznámení nemusí fungovat správně." + "Kontrola blokovaných uživatelů" + "Zobrazit blokované uživatele" + "Žádní uživatelé nejsou blokováni." + + "Zablokovali jste %1$d uživatele. Nebudete dostávat oznámení od tohoto uživatele." + "Zablokovali jste %1$d uživatele. Nebudete dostávat oznámení od těchto uživatelů." + "Zablokovali jste %1$d uživatelů. Nebudete dostávat oznámení od těchto uživatelů." + + "Blokovaní uživatelé" "Získat název aktuálního poskytovatele." "Nebyli vybráni žádní push poskytovatelé." "Aktuální push poskytovatel: %1$s." diff --git a/libraries/push/impl/src/main/res/values-cy/translations.xml b/libraries/push/impl/src/main/res/values-cy/translations.xml index d2e6a2810e..6ef6c0c8a8 100644 --- a/libraries/push/impl/src/main/res/values-cy/translations.xml +++ b/libraries/push/impl/src/main/res/values-cy/translations.xml @@ -78,6 +78,18 @@ "Cydweddu\'n y cefndir" "Gwasanaethau Google" "Heb ganfod Google Play Services dilys. Efallai fydd hysbysiadau ddim yn gweithio\'n iawn." + "Gwirio defnyddwyr sydd wedi\'u rhwystro" + "Gweld defnyddwyr wedi\'u rhwystro" + "Does dim defnyddwyr wedi\'u rhwystro." + + "Rydych wedi rhwystro %1$d defnyddiwr. Fyddwch chi ddim yn derbyn hysbysiadau ar gyfer y defnyddiwr hwn." + "Rydych wedi rhwystro %1$d defnyddiwr. Fyddwch chi ddim yn derbyn hysbysiadau ar gyfer y defnyddiwr hwn." + "Rydych wedi rhwystro %1$d defnyddiwr. Fyddwch chi ddim yn derbyn hysbysiadau ar gyfer y defnyddiwr hwn." + "Rydych wedi rhwystro %1$d defnyddiwr. Fyddwch chi ddim yn derbyn hysbysiadau ar gyfer y defnyddiwr hwn." + "Rydych wedi rhwystro %1$d defnyddiwr. Fyddwch chi ddim yn derbyn hysbysiadau ar gyfer y defnyddiwr hwn." + "Rydych wedi rhwystro %1$d defnyddiwr. Fyddwch chi ddim yn derbyn hysbysiadau ar gyfer y defnyddiwr hwn." + + "Defnyddwyr wedi\'u rhwystro" "Cael enw\'r darparwr presennol." "Dim darparwyr gwthio wedi\'u dewis." "Darparwr gwthio presennol: %1$s." diff --git a/libraries/push/impl/src/main/res/values-da/translations.xml b/libraries/push/impl/src/main/res/values-da/translations.xml index facfc0eb3e..255605953b 100644 --- a/libraries/push/impl/src/main/res/values-da/translations.xml +++ b/libraries/push/impl/src/main/res/values-da/translations.xml @@ -54,9 +54,20 @@ "Synkronisering i baggrunden" "Google-tjenester" "Der blev ikke fundet nogen gyldige Google Play-tjenester. Notifikationer fungerer muligvis ikke korrekt." + "Kontrollerer blokerede brugere" + "Se blokerede brugere" + "Ingen brugere er blokeret." + + "Du har blokeret %1$d bruger. Du vil ikke modtage meddelelser fra denne bruger." + "Du har blokeret %1$d brugere. Du vil ikke modtage meddelelser fra disse brugere." + + "Blokerede brugere" "Få navnet på den aktuelle udbyder." "Ingen push-udbydere valgt." + "Nuværende push-udbyder: %1$s og nuværende distributør: %2$s. Men distributøren %3$s kan ikke findes. Måske er applikationen blevet afinstalleret?" + "Nuværende push-udbyder: %1$s, men der er ikke konfigureret nogen distributører." "Nuværende push-udbyder: %1$s." + "Nuværende push-udbyder: %1$s (%2$s)" "Nuværende push-udbyder" "Sørg for, at programmet understøtter mindst én push-udbyder." "Ingen push-udbyder understøttelse fundet." diff --git a/libraries/push/impl/src/main/res/values-de/translations.xml b/libraries/push/impl/src/main/res/values-de/translations.xml index 4b607e0c5e..dff6371773 100644 --- a/libraries/push/impl/src/main/res/values-de/translations.xml +++ b/libraries/push/impl/src/main/res/values-de/translations.xml @@ -54,9 +54,20 @@ "Hintergrundsynchronisation" "Google-Dienste" "Keine gültigen Google Play-Dienste gefunden. Benachrichtigungen funktionieren möglicherweise nicht richtig." + "Überprüfen von gesperrten Nutzern" + "Gesperrte Nutzer ansehen" + "Keine Nutzer sind gesperrt." + + "Du hast %1$d Nutzer gesperrt. Du wirst für diesen Nutzer keine Benachrichtigungen erhalten." + "Du hast %1$d Nutzer gesperrt. Du wirst für diese Nutzer keine Benachrichtigungen erhalten." + + "Gesperrte Nutzer" "Ermittele den Namen des aktuellen Anbieters." "Kein Dienst für Push-Benachrichtigungen ausgewählt." + "Aktueller Push-Dienst: %1$s und aktueller UnifiedPush-Distributor: %2$s. Aber der Distributor %3$s kann nicht gefunden werden. Vielleicht wurde die App deinstalliert?" + "Aktueller Push-Dienst: %1$s, aber kein UnifiedPush-Distributor konfiguriert." "Aktueller Push-Dienst: %1$s." + "Aktueller Push-Dienst: %1$s (%2$s)" "Aktueller Push-Dienst" "Stelle sicher, dass die Anwendung mindestens einen Push-Dienst hat." "Keine Unterstützung für Push-Dienst gefunden." diff --git a/libraries/push/impl/src/main/res/values-et/translations.xml b/libraries/push/impl/src/main/res/values-et/translations.xml index 0484f0008c..45d8e3c8c8 100644 --- a/libraries/push/impl/src/main/res/values-et/translations.xml +++ b/libraries/push/impl/src/main/res/values-et/translations.xml @@ -54,9 +54,20 @@ "Sünkroniseerimine taustal" "Google\'i teenused" "Google Play Services taustateenust ei leidu. Teavitused ei pruugi toimida korrektselt." + "Kontrollin blokeeritud kasutajaid" + "Vaata blokeeritud kasutajaid" + "Sa pole ühtegi kasutajat blokeerinud." + + "Sa oled blokeerinud %1$d kasutaja. Sa ei saa tema kohta teavitusi" + "Sa oled blokeerinud %1$d kasutajat. Sa ei saa tema kohta teavitusi" + + "Blokeeritud kasutajad" "Vali hetkel kasutatava tõuketeenuste pakkuja nimi." "Tõuketeenuste pakkujaid pole valitud." + "Praegune tõuketeenuste pakkuja on %1$s ja praegune levitaja on %2$s. Aga levitajat %3$s ei leidu - kas võib olla, et rakendus on eemaldatud?" + "Praegune tõuketeenuste pakkuja on %1$s, aga ühtegi levitajat pole seadistatud." "Hetkel kasutatav tõuketeenuste pakkuja: %1$s." + "Praegune tõuketeenuste pakkuja: %1$s (%2$s)" "Hetkel kasutatav tõuketeenuste pakkuja" "Palun taga selle rakenduse jaoks on seadistatud vähemalt üks tõuketeenuste pakkuja." "Ühtegi tõuketeenuste pakkujat ei leidu." diff --git a/libraries/push/impl/src/main/res/values-fi/translations.xml b/libraries/push/impl/src/main/res/values-fi/translations.xml index 3c38dde477..f3cec970d5 100644 --- a/libraries/push/impl/src/main/res/values-fi/translations.xml +++ b/libraries/push/impl/src/main/res/values-fi/translations.xml @@ -54,9 +54,20 @@ "Taustasynkronointi" "Googlen palvelut" "Kelvollisia Google Play -palveluita ei löytynyt. Ilmoitukset eivät ehkä toimi oikein." + "Estettyjen käyttäjien tarkistus" + "Näytä estetyt käyttäjät" + "Yhtään käyttäjää ei ole estetty." + + "Estit %1$d käyttäjän. Et saa ilmoituksia tältä käyttäjältä." + "Estit %1$d käyttäjää. Et saa ilmoituksia näiltä käyttäjiltä." + + "Estetyt käyttäjät" "Hakee nykyisen palveluntarjoajan nimen." "Push-palveluntarjoajia ei ole valittu." + "Nykyinen push-palveluntarjoaja: %1$s ja nykyinen jakelija: %2$s. Mutta jakelijaa %3$s ei löydy. Ehkä sovellus on poistettu?" + "Nykyinen push-palveluntarjoaja: %1$s, mutta jakelijoita ei ole määritetty." "Nykyinen push-palveluntarjoaja: %1$s." + "Nykyinen push-palveluntarjoaja: %1$s (%2$s)" "Nykyinen push-palveluntarjoaja" "Varmistaa, että sovelluksella on vähintään yksi push-palveluntarjoaja." "Push-palveluntarjoajia ei löytynyt." diff --git a/libraries/push/impl/src/main/res/values-fr/translations.xml b/libraries/push/impl/src/main/res/values-fr/translations.xml index 0069c1e5a7..5324597f8b 100644 --- a/libraries/push/impl/src/main/res/values-fr/translations.xml +++ b/libraries/push/impl/src/main/res/values-fr/translations.xml @@ -54,9 +54,20 @@ "Synchronisation en arrière-plan" "Services Google" "Aucun service Google Play valide n’a été trouvé. Les notifications peuvent ne pas fonctionner correctement." + "Vérification des utilisateurs bloqués" + "Voir les utilisateurs bloqués" + "Aucun utilisateur n’est bloqué." + + "Vous avez bloqué %1$d utilisateur. Vous ne recevrez pas de notifications pour cet utilisateur." + "Vous avez bloqué %1$d utilisateurs. Vous ne recevrez pas de notifications pour ces utilisateurs." + + "Utilisateurs bloqués" "Obtenir le nom du fournisseur de Push actuel." "Aucun fournisseur de Push n’est sélectionné." + "Fournisseur de Push actuel: %1$s et distributeur actuel: %2$s. Mais le distributeur %3$s est introuvable. L’application a peut-être été désinstallée?" + "Fournisseur de Push actuel: %1$s, mais aucun distributeur n’a été configuré." "Fournisseur de Push actuel : %1$s." + "Fournisseur de Push actuel: %1$s (%2$s)" "Fournisseur de Push actuel" "Vérifier que l’application possède au moins un fournisseur de Push." "Aucun fournisseur de Push n’a été trouvé." diff --git a/libraries/push/impl/src/main/res/values-hu/translations.xml b/libraries/push/impl/src/main/res/values-hu/translations.xml index c0aad928d4..fb067af5c4 100644 --- a/libraries/push/impl/src/main/res/values-hu/translations.xml +++ b/libraries/push/impl/src/main/res/values-hu/translations.xml @@ -54,6 +54,14 @@ "Háttérszinkronizálás" "Google szolgáltatások" "A Google Play szolgáltatások nem találhatók. Előfordulhat, hogy az értesítések nem működnek megfelelően." + "Letiltott felhasználók ellenőrzése" + "Letiltott felhasználók megtekintése" + "Nincs felhasználó letiltva." + + "Letiltotta %1$d felhasználót. Nem fog értesítéseket kapni erről a felhasználóról." + "Letiltott %1$d felhasználót. Nem fog értesítéseket kapni ezekről a felhasználókról." + + "Letiltott felhasználók" "A jelenlegi szolgáltató nevének lekérdezése." "Nincs kiválasztva leküldéses értesítési szolgáltató." "Jelenlegi leküldéses értesítési szolgáltató: %1$s." @@ -61,8 +69,8 @@ "Győződjön meg arról, hogy az alkalmazás legalább egy leküldéses értesítési szolgáltatóval rendelkezik." "Nem található leküldéses értesítési szolgáltató." - "%1$d leküldéses szolgáltató találva: %2$s" - "%1$d leküldéses szolgáltató találva: %2$s" + "%1$d leküldéses értesítési szolgáltató találva: %2$s" + "%1$d leküldéses értesítési szolgáltató találva: %2$s" "Az alkalmazás úgy lett összeállítva, hogy támogatja a következőket: %1$s" "Leküldéses értesítési szolgáltatók észlelése" @@ -78,5 +86,5 @@ "Hiba, nem lehet tesztelni a leküldéses értesítést." "Hiba, időtúllépés a leküldéses értesítésre való várakozás során." "A leküldéses értesítés folyamata %1$d ezredmásodpercig tartott." - "Tesztelje a leküldéses értesítés folyamatát" + "Leküldéses értesítés folyamatának tesztelése" diff --git a/libraries/push/impl/src/main/res/values-nb/translations.xml b/libraries/push/impl/src/main/res/values-nb/translations.xml index 1be74244aa..7f8cc41413 100644 --- a/libraries/push/impl/src/main/res/values-nb/translations.xml +++ b/libraries/push/impl/src/main/res/values-nb/translations.xml @@ -54,9 +54,19 @@ "Bakgrunnssynkronisering" "Google Services" "Ingen gyldige Google Play-tjenester funnet. Det kan hende at varsler ikke fungerer som de skal." + "Sjekker blokkerte brukere" + "Vis blokkerte brukere" + "Ingen brukere er blokkert." + + "Du blokkerte%1$d bruker. Du vil ikke motta varsler for denne brukeren." + "Du blokkerte%1$d brukere. Du vil ikke motta varsler for disse brukerne." + + "Blokkerte brukere" "Få navnet på den nåværende tilbyderen." "Ingen push-leverandører er valgt." + "Nåværende push-leverandør: %1$s, men ingen distributører er konfigurert." "Gjeldende push-leverandør: %1$s." + "Nåværende push-leverandør: %1$s (%2$s)" "Nåværende push-leverandør" "Påse at applikasjonen har minst én push-leverandør." "Ingen push-leverandører funnet." diff --git a/libraries/push/impl/src/main/res/values-pt/translations.xml b/libraries/push/impl/src/main/res/values-pt/translations.xml index c62a951fa6..1d0184e516 100644 --- a/libraries/push/impl/src/main/res/values-pt/translations.xml +++ b/libraries/push/impl/src/main/res/values-pt/translations.xml @@ -54,6 +54,14 @@ "Sincronização em segundo plano" "Serviços do Google Play" "Nenhuns Serviços do Google Play válidos encontrados. As notificações poderão não funcionar devidamente." + "A verificar utilizadores bloqueados" + "Ver utilizadores bloqueados" + "Sem utilizadores bloqueados." + + "Bloqueaste %1$d utilizador. Não receberás notificações dele." + "Bloqueaste %1$d utilizadores. Não receberás notificações deles." + + "Utilizadores bloqueados" "Obtém o nome do fornecedor atual." "Nenhum fornecedor de envio selecionado." "Fornecedor de envio atual: %1$s." diff --git a/libraries/push/impl/src/main/res/values-ro/translations.xml b/libraries/push/impl/src/main/res/values-ro/translations.xml index 00986e689a..7a5bcf2f06 100644 --- a/libraries/push/impl/src/main/res/values-ro/translations.xml +++ b/libraries/push/impl/src/main/res/values-ro/translations.xml @@ -54,9 +54,21 @@ "Sincronizare în fundal" "Servicii Google" "Nu au fost găsite servicii Google Play valide. Este posibil ca notificările să nu funcționeze corect." + "Se verifică utilizatorii blocați" + "Vizualizați utilizatorii blocați" + "Niciun utilizator nu este blocat." + + "Ai blocat%1$d utilizator. Nu veți primi notificări pentru acest utilizator." + "Ai blocat%1$d utilizatori. Nu veți primi notificări pentru acești utilizatori." + "Ai blocat%1$d utilizatori. Nu veți primi notificări pentru acești utilizatori." + + "Utilizatori blocați" "Obțineți numele furnizorului curent." "Niciun furnizor push selectat." + "Furnizorul actual de push: %1$s și distribuitorul actual: %2$s. Dar distribuitorul %3$s nu este găsit. Poate că aplicația a fost dezinstalată?" + "Furnizor push actual: %1$s, dar nu au fost configurați distribuitori." "Furnizor de push actual: %1$s." + "Furnizor actual de push: %1$s (%2$s)" "Furnizor de push curent" "Asigurați-vă că aplicația are cel puțin un furnizor push." "Nu s-au găsit furnizori push." diff --git a/libraries/push/impl/src/main/res/values-ru/translations.xml b/libraries/push/impl/src/main/res/values-ru/translations.xml index 1cb9873b4f..60a9d965f0 100644 --- a/libraries/push/impl/src/main/res/values-ru/translations.xml +++ b/libraries/push/impl/src/main/res/values-ru/translations.xml @@ -60,9 +60,21 @@ "Фоновая синхронизация" "Сервисы Google" "Не найдены действующие службы Google Play. Уведомления могут работать некорректно." + "Проверка заблокированных пользователей" + "Просмотреть заблокированных пользователей" + "Ни один пользователь не заблокирован." + + "Вы заблокировали пользователя %1$d. Вы не будете получать уведомления от этого пользователей." + "Вы заблокировали пользователей %1$d. Вы не будете получать уведомления от этих пользователей." + "Вы заблокировали пользователей %1$d. Вы не будете получать уведомления от этих пользователей." + + "Заблокированные пользователи" "Получение имени текущего поставщика." "Поставщики push-уведомлений не выбраны." + "Текущий поставщик push-уведомлений: %1$s и текущий дистрибьютор: %2$s . Но дистрибьютор %3$s не найдено. Возможно, приложение было удалено?" + "Текущий поставщик push-уведомлений: %1$s , но ни один дистрибьютор не был настроен." "Текущий поставщик push-уведомлений: %1$s." + "Текущий поставщик push-уведомлений: %1$s (%2$s)" "Текущий поставщик push-уведомлений" "Убедитесь, что у приложения есть хотя бы один поставщик push-сообщений." "Поставщики push-уведомлений не найдены." diff --git a/libraries/push/impl/src/main/res/values-zh/translations.xml b/libraries/push/impl/src/main/res/values-zh/translations.xml index b4e7bfb08c..2fe8d3e4c7 100644 --- a/libraries/push/impl/src/main/res/values-zh/translations.xml +++ b/libraries/push/impl/src/main/res/values-zh/translations.xml @@ -48,6 +48,9 @@ "后台同步" "谷歌服务" "找不到有效的 Google Play 服务。通知可能无法正常工作。" + + "您已屏蔽 %1$d 位用户。您将不再收到这些用户的推送通知。" + "获取当前推送提供者的名称。" "未选择任何推送提供者。" "当前推送提供者:%1$s。" diff --git a/libraries/push/impl/src/main/res/values/localazy.xml b/libraries/push/impl/src/main/res/values/localazy.xml index 8e9da34b73..45a3235ad5 100644 --- a/libraries/push/impl/src/main/res/values/localazy.xml +++ b/libraries/push/impl/src/main/res/values/localazy.xml @@ -64,7 +64,10 @@ "Blocked users" "Get the name of the current provider." "No push providers selected." + "Current push provider: %1$s and current distributor: %2$s. But the distributor %3$s is not found. Maybe the application has been uninstalled?" + "Current push provider: %1$s, but no distributors have been configured." "Current push provider: %1$s." + "Current push provider: %1$s (%2$s)" "Current push provider" "Ensure that the application supports at least one push provider." "No push provider support found." diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannelsTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannelsTest.kt index a6b8bc9985..b93bbcaf15 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannelsTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannelsTest.kt @@ -18,6 +18,7 @@ import io.mockk.verify import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) @@ -66,5 +67,6 @@ class NotificationChannelsTest { ) = DefaultNotificationChannels( notificationManager = notificationManager, stringProvider = FakeStringProvider(), + context = RuntimeEnvironment.getApplication(), ) } 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 5fd1b6386e..7786fb261e 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 @@ -332,5 +332,9 @@ fun createNotificationCreator( fun createNotificationChannels(): NotificationChannels { val context = RuntimeEnvironment.getApplication() - return DefaultNotificationChannels(NotificationManagerCompat.from(context), FakeStringProvider("")) + return DefaultNotificationChannels( + notificationManager = NotificationManagerCompat.from(context), + stringProvider = FakeStringProvider(""), + context = context, + ) } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/CurrentPushProviderTestTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/CurrentPushProviderTestTest.kt index 95bfced7de..a11c6b147e 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/CurrentPushProviderTestTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/CurrentPushProviderTestTest.kt @@ -8,7 +8,10 @@ package io.element.android.libraries.push.impl.troubleshoot import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.push.test.FakeGetCurrentPushProvider +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.push.test.FakePushService +import io.element.android.libraries.pushproviders.api.Distributor +import io.element.android.libraries.pushproviders.test.FakePushProvider import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState import io.element.android.libraries.troubleshoot.test.runAndTestState import io.element.android.services.toolbox.test.strings.FakeStringProvider @@ -17,10 +20,18 @@ import org.junit.Test class CurrentPushProviderTestTest { @Test - fun `test CurrentPushProviderTest with a push provider`() = runTest { + fun `test CurrentPushProviderTest with a push provider and a distributor`() = runTest { val sut = CurrentPushProviderTest( - getCurrentPushProvider = FakeGetCurrentPushProvider("foo"), + pushService = FakePushService( + currentPushProvider = { + FakePushProvider( + name = "foo", + currentDistributorValue = { "aDistributor" }, + ) + } + ), stringProvider = FakeStringProvider(), + sessionId = A_SESSION_ID, ) sut.runAndTestState { assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true)) @@ -31,11 +42,86 @@ class CurrentPushProviderTestTest { } } + @Test + fun `test CurrentPushProviderTest with a push provider supporting multiple distributors, distributor found`() = runTest { + val sut = CurrentPushProviderTest( + pushService = FakePushService( + currentPushProvider = { + FakePushProvider( + name = "foo", + currentDistributorValue = { "aDistributor" }, + supportMultipleDistributors = true, + distributors = listOf(Distributor("aDistributor", "aDistributor")) + ) + }, + ), + stringProvider = FakeStringProvider(), + sessionId = A_SESSION_ID, + ) + sut.runAndTestState { + assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true)) + assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress) + val lastItem = awaitItem() + assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Success) + assertThat(lastItem.description).contains("foo") + } + } + + @Test + fun `test CurrentPushProviderTest with a push provider supporting multiple distributors, no distributor`() = runTest { + val sut = CurrentPushProviderTest( + pushService = FakePushService( + currentPushProvider = { + FakePushProvider( + name = "foo", + currentDistributorValue = { null }, + supportMultipleDistributors = true, + ) + }, + ), + stringProvider = FakeStringProvider(), + sessionId = A_SESSION_ID, + ) + sut.runAndTestState { + assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true)) + assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress) + val lastItem = awaitItem() + assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure()) + } + } + + @Test + fun `test CurrentPushProviderTest with a push provider supporting multiple distributors, distributor not found`() = runTest { + val sut = CurrentPushProviderTest( + pushService = FakePushService( + currentPushProvider = { + FakePushProvider( + name = "foo", + currentDistributorValue = { "aDistributor" }, + supportMultipleDistributors = true, + distributors = emptyList() + ) + }, + ), + stringProvider = FakeStringProvider(), + sessionId = A_SESSION_ID, + ) + sut.runAndTestState { + assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true)) + assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress) + val lastItem = awaitItem() + assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure()) + } + } + @Test fun `test CurrentPushProviderTest without push provider`() = runTest { val sut = CurrentPushProviderTest( - getCurrentPushProvider = FakeGetCurrentPushProvider(null), + pushService = FakePushService( + currentPushProvider = { null }, + ), stringProvider = FakeStringProvider(), + sessionId = A_SESSION_ID, ) sut.runAndTestState { assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true)) diff --git a/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PushProvider.kt b/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PushProvider.kt index bb6943d439..38a7135b75 100644 --- a/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PushProvider.kt +++ b/libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/pushproviders/api/PushProvider.kt @@ -24,6 +24,11 @@ interface PushProvider { */ val name: String + /** + * true if the Push provider supports multiple distributors. + */ + val supportMultipleDistributors: Boolean + /** * Return the list of available distributors. */ @@ -34,6 +39,11 @@ interface PushProvider { */ suspend fun registerWith(matrixClient: MatrixClient, distributor: Distributor): Result + /** + * Return the current distributor, or null if none. + */ + suspend fun getCurrentDistributorValue(sessionId: SessionId): String? + /** * Return the current distributor, or null if none. */ diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProvider.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProvider.kt index 806eb98bf5..4f8c99cd3e 100644 --- a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProvider.kt +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProvider.kt @@ -32,6 +32,7 @@ class FirebasePushProvider( ) : PushProvider { override val index = FirebaseConfig.INDEX override val name = FirebaseConfig.NAME + override val supportMultipleDistributors = false override fun getDistributors(): List { return listOfNotNull( @@ -54,6 +55,8 @@ class FirebasePushProvider( ) } + override suspend fun getCurrentDistributorValue(sessionId: SessionId): String = firebaseDistributor.value + override suspend fun getCurrentDistributor(sessionId: SessionId) = firebaseDistributor override suspend fun unregister(matrixClient: MatrixClient): Result { diff --git a/libraries/pushproviders/firebase/src/main/res/values-bg/translations.xml b/libraries/pushproviders/firebase/src/main/res/values-bg/translations.xml new file mode 100644 index 0000000000..7efaff4545 --- /dev/null +++ b/libraries/pushproviders/firebase/src/main/res/values-bg/translations.xml @@ -0,0 +1,7 @@ + + + "Уверете се, че Firebase е наличен." + "Firebase не е наличен." + "Firebase е наличен." + "Проверка на Firebase" + diff --git a/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt b/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt index 86792457ce..afb4d833d4 100644 --- a/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt +++ b/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt @@ -17,7 +17,9 @@ import io.element.android.tests.testutils.lambda.lambdaError class FakePushProvider( override val index: Int = 0, override val name: String = "aFakePushProvider", + override val supportMultipleDistributors: Boolean = false, private val distributors: List = listOf(Distributor("aDistributorValue", "aDistributorName")), + private val currentDistributorValue: () -> String? = { lambdaError() }, private val currentDistributor: () -> Distributor? = { distributors.firstOrNull() }, private val currentUserPushConfig: CurrentUserPushConfig? = null, private val registerWithResult: (MatrixClient, Distributor) -> Result = { _, _ -> lambdaError() }, @@ -32,6 +34,10 @@ class FakePushProvider( return registerWithResult(matrixClient, distributor) } + override suspend fun getCurrentDistributorValue(sessionId: SessionId): String? { + return currentDistributorValue() + } + override suspend fun getCurrentDistributor(sessionId: SessionId): Distributor? { return currentDistributor() } diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProvider.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProvider.kt index 03b754e26f..92d7c9ebc3 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProvider.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProvider.kt @@ -29,6 +29,7 @@ class UnifiedPushProvider( ) : PushProvider { override val index = UnifiedPushConfig.INDEX override val name = UnifiedPushConfig.NAME + override val supportMultipleDistributors = true override fun getDistributors(): List { return unifiedPushDistributorProvider.getDistributors() @@ -42,6 +43,10 @@ class UnifiedPushProvider( } } + override suspend fun getCurrentDistributorValue(sessionId: SessionId): String? { + return unifiedPushStore.getDistributorValue(sessionId) + } + override suspend fun getCurrentDistributor(sessionId: SessionId): Distributor? { val distributorValue = unifiedPushStore.getDistributorValue(sessionId) return getDistributors().find { it.value == distributorValue } diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt index e790efaaad..a7e6dc0cb0 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt @@ -13,7 +13,7 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs @@ -23,7 +23,7 @@ import io.element.android.libraries.roomselect.api.RoomSelectEntryPoint import io.element.android.libraries.roomselect.api.RoomSelectMode @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class RoomSelectNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt index 50612c8478..ffd3fda5c0 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt @@ -18,7 +18,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.matrix.ui.model.SelectRoomInfo @@ -27,7 +27,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -@Inject +@AssistedInject class RoomSelectPresenter( @Assisted private val mode: RoomSelectMode, private val dataSource: RoomSelectSearchDataSource, diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectSearchDataSource.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectSearchDataSource.kt index 58ee601548..9e636ead7b 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectSearchDataSource.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectSearchDataSource.kt @@ -16,8 +16,8 @@ import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.loadAllIncrementally import io.element.android.libraries.matrix.ui.model.SelectRoomInfo import io.element.android.libraries.matrix.ui.model.toSelectRoomInfo -import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOn @@ -40,13 +40,13 @@ class RoomSelectSearchDataSource( source = RoomList.Source.All, ) - val roomInfoList: Flow> = roomList.filteredSummaries + val roomInfoList: Flow> = roomList.filteredSummaries .map { roomSummaries -> roomSummaries .filter { it.info.currentUserMembership == CurrentUserMembership.JOINED } .distinctBy { it.roomId } // This should be removed once we're sure no duplicate Rooms can be received .map { roomSummary -> roomSummary.toSelectRoomInfo() } - .toPersistentList() + .toImmutableList() } .flowOn(coroutineDispatchers.computation) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt index 7d90629a50..a14ac7ab66 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt @@ -53,7 +53,7 @@ import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.roomselect.api.RoomSelectMode import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList @Suppress("MultipleEmitters") // False positive @OptIn(ExperimentalMaterial3Api::class) @@ -214,7 +214,7 @@ private fun RoomSummaryView( avatarType = AvatarType.Room( heroes = roomInfo.heroes.map { user -> user.getAvatarData(size = AvatarSize.RoomSelectRoomListItem) - }.toPersistentList(), + }.toImmutableList(), isTombstoned = roomInfo.isTombstoned, ), ) diff --git a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionData.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionData.kt index c33c0d8861..90b0a0b7c7 100644 --- a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionData.kt +++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionData.kt @@ -39,4 +39,12 @@ data class SessionData( val sessionPath: String, /** The path to the cache data stored for the session in the filesystem. */ val cachePath: String, + /** The position, to be able to order account. */ + val position: Long, + /** The index of the last date of session usage. */ + val lastUsageIndex: Long, + /** The optional display name of the user. */ + val userDisplayName: String?, + /** The optional avatar URL of the user. */ + val userAvatarUrl: String?, ) diff --git a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt index 50e9c6f787..9d9f143e15 100644 --- a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt +++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt @@ -11,8 +11,22 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map interface SessionStore { + /** + * A flow emitting the current logged in state. + * If there is at least one session, the state is [LoggedInState.LoggedIn] with the latest used session. + * If there is no session, the state is [LoggedInState.NotLoggedIn]. + */ fun loggedInStateFlow(): Flow + + /** + * Return a flow of all sessions ordered by last usage descending. + */ fun sessionsFlow(): Flow> + + /** + * Add a new session. If other sessions exist, the new one will be set as the latest used one, and + * the added session position will be set to a value higher than the other session positions. + */ suspend fun addSession(sessionData: SessionData) /** @@ -20,9 +34,35 @@ interface SessionStore { * No op if userId is not found in DB. */ suspend fun updateData(sessionData: SessionData) + + /** + * Update the user profile info of the session matching the userId. + */ + suspend fun updateUserProfile(sessionId: String, displayName: String?, avatarUrl: String?) + + /** + * Get the session data matching the userId, or null if not found. + */ suspend fun getSession(sessionId: String): SessionData? + + /** + * Get all sessions ordered by last usage descending. + */ suspend fun getAllSessions(): List + + /** + * Get the latest session, or null if no session exists. + */ suspend fun getLatestSession(): SessionData? + + /** + * Set the session with [sessionId] as the latest used one. + */ + suspend fun setLatestSession(sessionId: String) + + /** + * Remove the session matching the sessionId. + */ suspend fun removeSession(sessionId: String) } diff --git a/libraries/session-storage/impl/build.gradle.kts b/libraries/session-storage/impl/build.gradle.kts index b9f96e1105..3ceb4076cc 100644 --- a/libraries/session-storage/impl/build.gradle.kts +++ b/libraries/session-storage/impl/build.gradle.kts @@ -36,7 +36,7 @@ dependencies { sqldelight { databases { create("SessionDatabase") { - // https://cashapp.github.io/sqldelight/2.0.0/android_sqlite/migrations/ + // https://sqldelight.github.io/sqldelight/2.1.0/android_sqlite/migrations/ // To generate a .db file from your latest schema, run this task // ./gradlew generateDebugSessionDatabaseSchema // Test migration by running diff --git a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt index f74676427d..d6197d868d 100644 --- a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt +++ b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt @@ -34,7 +34,7 @@ class DatabaseSessionStore( private val sessionDataMutex = Mutex() override fun loggedInStateFlow(): Flow { - return database.sessionDataQueries.selectFirst() + return database.sessionDataQueries.selectLatest() .asFlow() .mapToOneOrNull(dispatchers.io) .map { @@ -51,7 +51,17 @@ class DatabaseSessionStore( override suspend fun addSession(sessionData: SessionData) { sessionDataMutex.withLock { - database.sessionDataQueries.insertSessionData(sessionData.toDbModel()) + val lastUsageIndex = getLastUsageIndex() + database.sessionDataQueries.insertSessionData( + sessionData + .copy( + // position value does not really matter, so just use lastUsageIndex + 1 to ensure that + // the value is always greater than value of any existing account + position = lastUsageIndex + 1, + lastUsageIndex = lastUsageIndex + 1, + ) + .toDbModel() + ) } } @@ -65,18 +75,71 @@ class DatabaseSessionStore( Timber.e("User ${sessionData.userId} not found in session database") return } - // Copy new data from SDK, but keep login timestamp + // Copy new data from SDK, but keep application data database.sessionDataQueries.updateSession( sessionData.copy( loginTimestamp = result.loginTimestamp, + position = result.position, + lastUsageIndex = result.lastUsageIndex, + userDisplayName = result.userDisplayName, + userAvatarUrl = result.userAvatarUrl, ).toDbModel() ) } } + override suspend fun updateUserProfile(sessionId: String, displayName: String?, avatarUrl: String?) { + sessionDataMutex.withLock { + val result = database.sessionDataQueries.selectByUserId(sessionId) + .executeAsOneOrNull() + ?.toApiModel() + if (result == null) { + Timber.e("User $sessionId not found in session database") + return + } + database.sessionDataQueries.updateSession( + result.copy( + userDisplayName = displayName, + userAvatarUrl = avatarUrl, + ).toDbModel() + ) + } + } + + override suspend fun setLatestSession(sessionId: String) { + val latestSession = getLatestSession() + if (latestSession?.userId == sessionId) { + // Already the latest session + return + } + val lastUsageIndex = latestSession?.lastUsageIndex ?: 0 + val result = database.sessionDataQueries.selectByUserId(sessionId) + .executeAsOneOrNull() + ?.toApiModel() + if (result == null) { + Timber.e("User $sessionId not found in session database") + return + } + sessionDataMutex.withLock { + // Update lastUsageIndex of the session + database.sessionDataQueries.updateSession( + result.copy( + lastUsageIndex = lastUsageIndex + 1, + ).toDbModel() + ) + } + } + + private fun getLastUsageIndex(): Long { + return database.sessionDataQueries.selectLatest() + .executeAsOneOrNull() + ?.lastUsageIndex + ?: -1L + } + override suspend fun getLatestSession(): SessionData? { return sessionDataMutex.withLock { - database.sessionDataQueries.selectFirst() + database.sessionDataQueries.selectLatest() .executeAsOneOrNull() ?.toApiModel() } diff --git a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/SessionDataMapper.kt b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/SessionDataMapper.kt index 8dbbad2b71..3b694c0124 100644 --- a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/SessionDataMapper.kt +++ b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/SessionDataMapper.kt @@ -27,6 +27,10 @@ internal fun SessionData.toDbModel(): DbSessionData { passphrase = passphrase, sessionPath = sessionPath, cachePath = cachePath, + position = position, + lastUsageIndex = lastUsageIndex, + userDisplayName = userDisplayName, + userAvatarUrl = userAvatarUrl, ) } @@ -45,5 +49,9 @@ internal fun DbSessionData.toApiModel(): SessionData { passphrase = passphrase, sessionPath = sessionPath, cachePath = cachePath, + position = position, + lastUsageIndex = lastUsageIndex, + userDisplayName = userDisplayName, + userAvatarUrl = userAvatarUrl, ) } diff --git a/libraries/session-storage/impl/src/main/sqldelight/databases/10.db b/libraries/session-storage/impl/src/main/sqldelight/databases/10.db new file mode 100644 index 0000000000..fe31cc0fac Binary files /dev/null and b/libraries/session-storage/impl/src/main/sqldelight/databases/10.db differ diff --git a/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq b/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq index 6e4c817475..53d07bfba3 100644 --- a/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq +++ b/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq @@ -27,15 +27,25 @@ CREATE TABLE SessionData ( -- added in version 6 sessionPath TEXT NOT NULL DEFAULT "", -- added in version 9 - cachePath TEXT NOT NULL DEFAULT "" + cachePath TEXT NOT NULL DEFAULT "", + -- added in version 10 + -- position, to be able to sort account by session creation date + position INTEGER NOT NULL DEFAULT 0, + -- index of the last usage session. Each time the current session change, the index of the current + -- session is incremented to the max value + 1 so it becomes the current session + lastUsageIndex INTEGER NOT NULL DEFAULT 0, + -- user display name + userDisplayName TEXT, + -- user avatar url + userAvatarUrl TEXT ); -selectFirst: -SELECT * FROM SessionData LIMIT 1; +selectLatest: +SELECT * FROM SessionData ORDER BY lastUsageIndex DESC LIMIT 1; selectAll: -SELECT * FROM SessionData; +SELECT * FROM SessionData ORDER BY lastUsageIndex DESC; selectByUserId: SELECT * FROM SessionData WHERE userId = ?; diff --git a/libraries/session-storage/impl/src/main/sqldelight/migrations/9.sqm b/libraries/session-storage/impl/src/main/sqldelight/migrations/9.sqm new file mode 100644 index 0000000000..51c1366525 --- /dev/null +++ b/libraries/session-storage/impl/src/main/sqldelight/migrations/9.sqm @@ -0,0 +1,9 @@ +-- Migrate DB from version 9 +-- Add position to be able to sort account by session creation date +-- Add lastUsageIndex so we can restore the last session and switch to another one +-- Add display name and avatar url of the user so that we can display a list of accounts. + +ALTER TABLE SessionData ADD COLUMN position INTEGER NOT NULL DEFAULT 0; +ALTER TABLE SessionData ADD COLUMN lastUsageIndex INTEGER NOT NULL DEFAULT 0; +ALTER TABLE SessionData ADD COLUMN userDisplayName TEXT; +ALTER TABLE SessionData ADD COLUMN userAvatarUrl TEXT; diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTest.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTest.kt index f05b15a0b7..7d264f42db 100644 --- a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTest.kt +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTest.kt @@ -13,6 +13,7 @@ import com.google.common.truth.Truth.assertThat import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.session.SessionData import io.element.android.libraries.sessionstorage.api.LoggedInState +import io.element.android.libraries.sessionstorage.api.LoginType import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest @@ -45,11 +46,11 @@ class DatabaseSessionStoreTest { @Test fun `addSession persists the SessionData into the DB`() = runTest { - assertThat(database.sessionDataQueries.selectFirst().executeAsOneOrNull()).isNull() + assertThat(database.sessionDataQueries.selectLatest().executeAsOneOrNull()).isNull() databaseSessionStore.addSession(aSessionData.toApiModel()) - assertThat(database.sessionDataQueries.selectFirst().executeAsOneOrNull()).isEqualTo(aSessionData) + assertThat(database.sessionDataQueries.selectLatest().executeAsOneOrNull()).isEqualTo(aSessionData) assertThat(database.sessionDataQueries.selectAll().executeAsList().size).isEqualTo(1) } @@ -59,7 +60,12 @@ class DatabaseSessionStoreTest { assertThat(awaitItem()).isEqualTo(LoggedInState.NotLoggedIn) databaseSessionStore.addSession(aSessionData.toApiModel()) assertThat(awaitItem()).isEqualTo(LoggedInState.LoggedIn(sessionId = aSessionData.userId, isTokenValid = true)) - // TODO add more sessions in multi-account PR. + // Add a second session + databaseSessionStore.addSession(aSessionData.copy(userId = "otherUserId").toApiModel()) + assertThat(awaitItem()).isEqualTo(LoggedInState.LoggedIn(sessionId = "otherUserId", isTokenValid = true)) + // Remove the second session + databaseSessionStore.removeSession("otherUserId") + assertThat(awaitItem()).isEqualTo(LoggedInState.LoggedIn(sessionId = aSessionData.userId, isTokenValid = true)) // Remove the first session databaseSessionStore.removeSession(aSessionData.userId) assertThat(awaitItem()).isEqualTo(LoggedInState.NotLoggedIn) @@ -124,7 +130,83 @@ class DatabaseSessionStoreTest { } @Test - fun `update session update all fields except loginTimestamp`() = runTest { + fun `updateUserProfile does nothing if the session is not found`() = runTest { + databaseSessionStore.updateUserProfile(aSessionData.userId, "userDisplayName", "userAvatarUrl") + assertThat(database.sessionDataQueries.selectByUserId(aSessionData.userId).executeAsOneOrNull()).isNull() + } + + @Test + fun `updateUserProfile update the data`() = runTest { + database.sessionDataQueries.insertSessionData(aSessionData) + databaseSessionStore.updateUserProfile(aSessionData.userId, "userDisplayName", "userAvatarUrl") + val updatedSession = database.sessionDataQueries.selectByUserId(aSessionData.userId).executeAsOne() + assertThat(updatedSession.userDisplayName).isEqualTo("userDisplayName") + assertThat(updatedSession.userAvatarUrl).isEqualTo("userAvatarUrl") + } + + @Test + fun `setLatestSession is no op when the session is already the latest session`() = runTest { + database.sessionDataQueries.insertSessionData(aSessionData) + val session = database.sessionDataQueries.selectByUserId(aSessionData.userId).executeAsOne() + assertThat(session.lastUsageIndex).isEqualTo(0) + assertThat(session.position).isEqualTo(0) + databaseSessionStore.setLatestSession(aSessionData.userId) + assertThat(database.sessionDataQueries.selectByUserId(aSessionData.userId).executeAsOne().lastUsageIndex).isEqualTo(0) + } + + @Test + fun `setLatestSession is no op when the session is not found`() = runTest { + databaseSessionStore.setLatestSession(aSessionData.userId) + } + + @Test + fun `multi session test`() = runTest { + databaseSessionStore.addSession(aSessionData.toApiModel()) + val session = databaseSessionStore.getSession(aSessionData.userId)!! + assertThat(session.lastUsageIndex).isEqualTo(0) + assertThat(session.position).isEqualTo(0) + val secondSessionData = aSessionData.copy( + userId = "otherUserId", + position = 1, + lastUsageIndex = 1, + ) + databaseSessionStore.addSession(secondSessionData.toApiModel()) + val secondSession = database.sessionDataQueries.selectByUserId(secondSessionData.userId).executeAsOne() + assertThat(secondSession.lastUsageIndex).isEqualTo(1) + assertThat(secondSession.position).isEqualTo(1) + // Set the first session as the latest + databaseSessionStore.setLatestSession(aSessionData.userId) + val firstSession = database.sessionDataQueries.selectByUserId(aSessionData.userId).executeAsOne() + assertThat(firstSession.lastUsageIndex).isEqualTo(2) + assertThat(firstSession.position).isEqualTo(0) + // Check that the second session has not been altered + val secondSession2 = database.sessionDataQueries.selectByUserId(secondSessionData.userId).executeAsOne() + assertThat(secondSession2.lastUsageIndex).isEqualTo(1) + assertThat(secondSession2.position).isEqualTo(1) + } + + @Test + fun `test sessionsFlow()`() = runTest { + databaseSessionStore.sessionsFlow().test { + assertThat(awaitItem()).isEmpty() + databaseSessionStore.addSession(aSessionData.toApiModel()) + assertThat(awaitItem().size).isEqualTo(1) + val secondSessionData = aSessionData.copy( + userId = "otherUserId", + position = 1, + lastUsageIndex = 1, + ) + databaseSessionStore.addSession(secondSessionData.toApiModel()) + assertThat(awaitItem().size).isEqualTo(2) + databaseSessionStore.removeSession(aSessionData.userId) + assertThat(awaitItem().size).isEqualTo(1) + databaseSessionStore.removeSession(secondSessionData.userId) + assertThat(awaitItem()).isEmpty() + } + } + + @Test + fun `update session update all fields except info used by the application`() = runTest { val firstSessionData = SessionData( userId = "userId", deviceId = "deviceId", @@ -139,6 +221,10 @@ class DatabaseSessionStoreTest { passphrase = "aPassphrase", sessionPath = "sessionPath", cachePath = "cachePath", + position = 0, + lastUsageIndex = 0, + userDisplayName = "userDisplayName", + userAvatarUrl = "userAvatarUrl", ) val secondSessionData = SessionData( userId = "userId", @@ -152,8 +238,12 @@ class DatabaseSessionStoreTest { isTokenValid = 1, loginType = null, passphrase = "aPassphraseAltered", - sessionPath = "sessionPath", - cachePath = "cachePath", + sessionPath = "sessionPathAltered", + cachePath = "cachePathAltered", + position = 1, + lastUsageIndex = 1, + userDisplayName = "userDisplayNameAltered", + userAvatarUrl = "userAvatarUrlAltered", ) assertThat(firstSessionData.userId).isEqualTo(secondSessionData.userId) assertThat(firstSessionData.loginTimestamp).isNotEqualTo(secondSessionData.loginTimestamp) @@ -174,6 +264,11 @@ class DatabaseSessionStoreTest { assertThat(alteredSession.loginTimestamp).isEqualTo(firstSessionData.loginTimestamp) assertThat(alteredSession.oidcData).isEqualTo(secondSessionData.oidcData) assertThat(alteredSession.passphrase).isEqualTo(secondSessionData.passphrase) + // Check that application data have not been altered + assertThat(alteredSession.position).isEqualTo(firstSessionData.position) + assertThat(alteredSession.lastUsageIndex).isEqualTo(firstSessionData.lastUsageIndex) + assertThat(alteredSession.userDisplayName).isEqualTo(firstSessionData.userDisplayName) + assertThat(alteredSession.userAvatarUrl).isEqualTo(firstSessionData.userAvatarUrl) } @Test @@ -188,10 +283,14 @@ class DatabaseSessionStoreTest { loginTimestamp = 1, oidcData = "aOidcData", isTokenValid = 1, - loginType = null, + loginType = LoginType.PASSWORD.name, passphrase = "aPassphrase", sessionPath = "sessionPath", cachePath = "cachePath", + position = 0, + lastUsageIndex = 0, + userDisplayName = "userDisplayName", + userAvatarUrl = "userAvatarUrl", ) val secondSessionData = SessionData( userId = "userIdUnknown", @@ -203,10 +302,14 @@ class DatabaseSessionStoreTest { loginTimestamp = 2, oidcData = "aOidcDataAltered", isTokenValid = 1, - loginType = null, + loginType = LoginType.PASSWORD.name, passphrase = "aPassphraseAltered", - sessionPath = "sessionPath", - cachePath = "cachePath", + sessionPath = "sessionPathAltered", + cachePath = "cachePathAltered", + position = 1, + lastUsageIndex = 1, + userDisplayName = "userDisplayNameAltered", + userAvatarUrl = "userAvatarUrlAltered", ) assertThat(firstSessionData.userId).isNotEqualTo(secondSessionData.userId) @@ -216,14 +319,6 @@ class DatabaseSessionStoreTest { // Get the session and check that it has not been altered val notAlteredSession = databaseSessionStore.getSession(firstSessionData.userId)!!.toDbModel() - assertThat(notAlteredSession.userId).isEqualTo(firstSessionData.userId) - assertThat(notAlteredSession.deviceId).isEqualTo(firstSessionData.deviceId) - assertThat(notAlteredSession.accessToken).isEqualTo(firstSessionData.accessToken) - assertThat(notAlteredSession.refreshToken).isEqualTo(firstSessionData.refreshToken) - assertThat(notAlteredSession.homeserverUrl).isEqualTo(firstSessionData.homeserverUrl) - assertThat(notAlteredSession.slidingSyncProxy).isEqualTo(firstSessionData.slidingSyncProxy) - assertThat(notAlteredSession.loginTimestamp).isEqualTo(firstSessionData.loginTimestamp) - assertThat(notAlteredSession.oidcData).isEqualTo(firstSessionData.oidcData) - assertThat(notAlteredSession.passphrase).isEqualTo(firstSessionData.passphrase) + assertThat(notAlteredSession).isEqualTo(firstSessionData) } } diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt index 3251dfc5d9..e8713dac1a 100644 --- a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt @@ -24,4 +24,8 @@ internal fun aSessionData() = SessionData( passphrase = null, sessionPath = "sessionPath", cachePath = "cachePath", + position = 0, + lastUsageIndex = 0, + userDisplayName = null, + userAvatarUrl = null, ) diff --git a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemorySessionStore.kt b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemorySessionStore.kt index eb1645fddf..c8f3078e7a 100644 --- a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemorySessionStore.kt +++ b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemorySessionStore.kt @@ -17,6 +17,8 @@ import kotlinx.coroutines.flow.map class InMemorySessionStore( initialList: List = emptyList(), + private val updateUserProfileResult: (String, String?, String?) -> Unit = { _, _, _ -> error("Not implemented") }, + private val setLatestSessionResult: (String) -> Unit = { error("Not implemented") }, ) : SessionStore { private val sessionDataListFlow = MutableStateFlow(initialList) @@ -53,6 +55,10 @@ class InMemorySessionStore( } } + override suspend fun updateUserProfile(sessionId: String, displayName: String?, avatarUrl: String?) { + updateUserProfileResult(sessionId, displayName, avatarUrl) + } + override suspend fun getSession(sessionId: String): SessionData? { return sessionDataListFlow.value.firstOrNull { it.userId == sessionId } } @@ -65,6 +71,10 @@ class InMemorySessionStore( return sessionDataListFlow.value.firstOrNull() } + override suspend fun setLatestSession(sessionId: String) { + setLatestSessionResult(sessionId) + } + override suspend fun removeSession(sessionId: String) { val currentList = sessionDataListFlow.value.toMutableList() currentList.removeAll { it.userId == sessionId } diff --git a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/SessionData.kt b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/SessionData.kt index afff40b6e1..61b5370813 100644 --- a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/SessionData.kt +++ b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/SessionData.kt @@ -18,7 +18,11 @@ fun aSessionData( cachePath: String = "/a/path/to/a/cache", accessToken: String = "anAccessToken", refreshToken: String? = "aRefreshToken", - ): SessionData { + position: Long = 0, + lastUsageIndex: Long = 0, + userDisplayName: String? = null, + userAvatarUrl: String? = null, +): SessionData { return SessionData( userId = sessionId, deviceId = deviceId, @@ -33,5 +37,9 @@ fun aSessionData( passphrase = null, sessionPath = sessionPath, cachePath = cachePath, + position = position, + lastUsageIndex = lastUsageIndex, + userDisplayName = userDisplayName, + userAvatarUrl = userAvatarUrl, ) } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/LiveWaveformView.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/LiveWaveformView.kt index 6c35fef2b1..836089fc7d 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/LiveWaveformView.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/LiveWaveformView.kt @@ -35,7 +35,7 @@ import io.element.android.libraries.designsystem.components.media.drawWaveform import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import java.lang.Float.min private const val DEFAULT_GRAPHICS_LAYER_ALPHA: Float = 0.99F @@ -62,21 +62,21 @@ fun LiveWaveformView( Box( contentAlignment = Alignment.CenterEnd, modifier = modifier - .fillMaxWidth() - .height(waveFormHeight) - .onSizeChanged { parentWidth = it.width } + .fillMaxWidth() + .height(waveFormHeight) + .onSizeChanged { parentWidth = it.width } ) { Canvas( modifier = Modifier - .width(Dp(waveformWidth)) - .graphicsLayer(alpha = DEFAULT_GRAPHICS_LAYER_ALPHA) - .then(modifier) + .width(Dp(waveformWidth)) + .graphicsLayer(alpha = DEFAULT_GRAPHICS_LAYER_ALPHA) + .then(modifier) ) { val width = min(waveformWidth, parentWidth.toFloat()) canvasSize = DpSize(width.dp, size.height.toDp()) val countThatFitsWidth = (parentWidth.toFloat() / (lineWidth.toPx() + linePadding.toPx())).toInt() drawWaveform( - waveformData = levels.takeLast(countThatFitsWidth).toPersistentList(), + waveformData = levels.takeLast(countThatFitsWidth).toImmutableList(), canvasSizePx = Size(canvasSize.width.toPx(), size.height), brush = brush, lineWidth = lineWidth, @@ -91,11 +91,11 @@ fun LiveWaveformView( internal fun LiveWaveformViewPreview() = ElementPreview { Column { LiveWaveformView( - levels = List(100) { it.toFloat() / 100 }.toPersistentList(), + levels = List(100) { it.toFloat() / 100 }.toImmutableList(), modifier = Modifier.height(34.dp), ) LiveWaveformView( - levels = List(40) { it.toFloat() / 40 }.toPersistentList(), + levels = List(40) { it.toFloat() / 40 }.toImmutableList(), modifier = Modifier.height(34.dp), ) } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecording.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecording.kt index 30e2ab95c4..7a56d37b3c 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecording.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecording.kt @@ -35,7 +35,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.ui.utils.time.formatShort import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList +import kotlinx.collections.immutable.toImmutableList import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds @@ -101,5 +101,5 @@ private fun RedRecordingDot() { @PreviewsDayNight @Composable internal fun VoiceMessageRecordingPreview() = ElementPreview { - VoiceMessageRecording(List(100) { it.toFloat() / 100 }.toPersistentList(), 0.seconds) + VoiceMessageRecording(List(100) { it.toFloat() / 100 }.toImmutableList(), 0.seconds) } diff --git a/libraries/textcomposer/impl/src/main/res/values-bg/translations.xml b/libraries/textcomposer/impl/src/main/res/values-bg/translations.xml index 3079cc1054..b0dfde1c2e 100644 --- a/libraries/textcomposer/impl/src/main/res/values-bg/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-bg/translations.xml @@ -1,6 +1,8 @@ "Прикачване на файл" + "Отказ и затваряне на форматирането на текст" + "Превключване на кодов блок" "Съобщение…" "Създаване на връзка" "Редактиране на връзката" @@ -8,8 +10,15 @@ "Прилагане на курсив формат" "Прилагане на зачеркнат формат" "Прилагане на формат за подчертаване" + "Превключване на режим на цял екран" + "Отстъп навътре" "Прилагане на формат на вграден код" + "Задаване на връзка" + "Превключване на номериран списък" + "Отваряне на опциите за съставяне" "Превключване на цитат" "Премахване на връзката" + "Отстъп навън" "Връзка" + "Задръжте, за записване" diff --git a/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/PushHistoryEntryPoint.kt b/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/PushHistoryEntryPoint.kt index 088fb387da..0eab9b8e5a 100644 --- a/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/PushHistoryEntryPoint.kt +++ b/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/PushHistoryEntryPoint.kt @@ -13,7 +13,6 @@ import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.architecture.FeatureEntryPoint 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 interface PushHistoryEntryPoint : FeatureEntryPoint { fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder @@ -25,6 +24,6 @@ interface PushHistoryEntryPoint : FeatureEntryPoint { interface Callback : Plugin { fun onDone() - fun onItemClick(sessionId: SessionId, roomId: RoomId, eventId: EventId) + fun navigateTo(roomId: RoomId, eventId: EventId) } } diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsNode.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsNode.kt index fcd9306171..508010a3d6 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsNode.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsNode.kt @@ -14,7 +14,7 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode import io.element.android.libraries.di.SessionScope @@ -23,7 +23,7 @@ import io.element.android.libraries.troubleshoot.api.test.NotificationTroublesho import io.element.android.services.analytics.api.ScreenTracker @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class TroubleshootNotificationsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, 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 b1c3c2d9b9..07840b023c 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 @@ -14,12 +14,12 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator import kotlinx.coroutines.launch -@Inject +@AssistedInject class TroubleshootNotificationsPresenter( @Assisted private val navigator: NotificationTroubleshootNavigator, private val troubleshootTestSuite: TroubleshootTestSuite, 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/PushHistoryEvents.kt index c18a480899..893be607a0 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/PushHistoryEvents.kt @@ -7,8 +7,13 @@ package io.element.android.libraries.troubleshoot.impl.history +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 } diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryNode.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryNode.kt index 80b938898f..69070298ec 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryNode.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryNode.kt @@ -14,36 +14,37 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode 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.core.SessionId import io.element.android.libraries.troubleshoot.api.PushHistoryEntryPoint import io.element.android.services.analytics.api.ScreenTracker @ContributesNode(SessionScope::class) -@Inject +@AssistedInject class PushHistoryNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, - private val presenter: PushHistoryPresenter, + presenterFactory: PushHistoryPresenter.Factory, private val screenTracker: ScreenTracker, -) : Node(buildContext, plugins = plugins) { +) : Node(buildContext, plugins = plugins), PushHistoryNavigator { private fun onDone() { plugins().forEach { it.onDone() } } - private fun onItemClick(sessionId: SessionId, roomId: RoomId, eventId: EventId) { + override fun navigateTo(roomId: RoomId, eventId: EventId) { plugins().forEach { - it.onItemClick(sessionId, roomId, eventId) + it.navigateTo(roomId, eventId) } } + private val presenter = presenterFactory.create(this) + @Composable override fun View(modifier: Modifier) { screenTracker.TrackScreen(MobileScreen.ScreenName.NotificationTroubleshoot) @@ -51,7 +52,6 @@ class PushHistoryNode( PushHistoryView( state = state, onBackClick = ::onDone, - onItemClick = ::onItemClick, modifier = modifier, ) } 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 77c38435c3..b98fcee970 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 @@ -14,18 +14,36 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.Assisted +import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.push.api.PushService import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -@Inject +fun interface PushHistoryNavigator { + fun navigateTo(roomId: RoomId, eventId: EventId) +} + +@AssistedInject class PushHistoryPresenter( + @Assisted private val pushHistoryNavigator: PushHistoryNavigator, private val pushService: PushService, + matrixClient: MatrixClient, ) : Presenter { + @AssistedFactory + fun interface Factory { + fun create(pushHistoryNavigator: PushHistoryNavigator): PushHistoryPresenter + } + + private val sessionId = matrixClient.sessionId + @Composable override fun present(): PushHistoryState { val coroutineScope = rememberCoroutineScope() @@ -41,6 +59,7 @@ class PushHistoryPresenter( } }.collectAsState(emptyList()) var resetAction: AsyncAction by remember { mutableStateOf(AsyncAction.Uninitialized) } + var showNotSameAccountError by remember { mutableStateOf(false) } fun handleEvents(event: PushHistoryEvents) { when (event) { @@ -60,6 +79,14 @@ class PushHistoryPresenter( } PushHistoryEvents.ClearDialog -> { resetAction = AsyncAction.Uninitialized + showNotSameAccountError = false + } + is PushHistoryEvents.NavigateTo -> { + if (event.sessionId != sessionId) { + showNotSameAccountError = true + } else { + pushHistoryNavigator.navigateTo(event.roomId, event.eventId) + } } } } @@ -69,6 +96,7 @@ class PushHistoryPresenter( pushHistoryItems = pushHistory.toImmutableList(), showOnlyErrors = showOnlyErrors, resetAction = resetAction, + showNotSameAccountError = showNotSameAccountError, eventSink = ::handleEvents ) } 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 fda9c6e479..b4b6d7f75e 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 @@ -16,5 +16,6 @@ data class PushHistoryState( val pushHistoryItems: ImmutableList, val showOnlyErrors: Boolean, val resetAction: AsyncAction, + val showNotSameAccountError: Boolean, val eventSink: (PushHistoryEvents) -> Unit, ) diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryStateProvider.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryStateProvider.kt index da37700a93..11d9c509cc 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryStateProvider.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryStateProvider.kt @@ -40,6 +40,9 @@ open class PushHistoryStateProvider : PreviewParameterProvider aPushHistoryState( resetAction = AsyncAction.ConfirmingNoParams, ), + aPushHistoryState( + showNotSameAccountError = true, + ), ) } @@ -48,12 +51,14 @@ fun aPushHistoryState( pushHistoryItems: List = emptyList(), showOnlyErrors: Boolean = false, resetAction: AsyncAction = AsyncAction.Uninitialized, + showNotSameAccountError: Boolean = false, eventSink: (PushHistoryEvents) -> Unit = {}, ) = PushHistoryState( pushCounter = pushCounter, pushHistoryItems = pushHistoryItems.toImmutableList(), showOnlyErrors = showOnlyErrors, resetAction = resetAction, + showNotSameAccountError = showNotSameAccountError, eventSink = eventSink, ) 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 2cd2e6dc20..3193716d34 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 @@ -37,6 +37,7 @@ import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.components.button.BackButton 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.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -48,9 +49,6 @@ import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.matrix.api.core.EventId -import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.push.api.history.PushHistoryItem import io.element.android.libraries.troubleshoot.impl.R import io.element.android.libraries.ui.strings.CommonStrings @@ -60,7 +58,6 @@ import io.element.android.libraries.ui.strings.CommonStrings fun PushHistoryView( state: PushHistoryState, onBackClick: () -> Unit, - onItemClick: (SessionId, RoomId, EventId) -> Unit, modifier: Modifier = Modifier, ) { var showMenu by remember { mutableStateOf(false) } @@ -123,7 +120,6 @@ fun PushHistoryView( .padding(padding) .consumeWindowInsets(padding), state = state, - onItemClick = onItemClick, ) } @@ -142,12 +138,18 @@ fun PushHistoryView( }, onErrorDismiss = {}, ) + + if (state.showNotSameAccountError) { + ErrorDialog( + content = "Please switch account first to navigate to the event.", + onSubmit = { state.eventSink(PushHistoryEvents.ClearDialog) } + ) + } } @Composable private fun PushHistoryContent( state: PushHistoryState, - onItemClick: (SessionId, RoomId, EventId) -> Unit, modifier: Modifier = Modifier, ) { Column( @@ -173,7 +175,7 @@ private fun PushHistoryContent( val roomId = pushHistory.roomId val eventId = pushHistory.eventId if (sessionId != null && roomId != null && eventId != null) { - onItemClick(sessionId, roomId, eventId) + state.eventSink(PushHistoryEvents.NavigateTo(sessionId, roomId, eventId)) } } ) @@ -271,6 +273,5 @@ internal fun PushHistoryViewPreview( PushHistoryView( state = state, onBackClick = {}, - onItemClick = { _, _, _ -> }, ) } diff --git a/libraries/troubleshoot/impl/src/main/res/values-bg/translations.xml b/libraries/troubleshoot/impl/src/main/res/values-bg/translations.xml new file mode 100644 index 0000000000..3af1921802 --- /dev/null +++ b/libraries/troubleshoot/impl/src/main/res/values-bg/translations.xml @@ -0,0 +1,7 @@ + + + "Изпълняване на тестове" + "Изпълняване на тестовете отново" + "Всички тестове преминаха успешно." + "Отстраняване на неизправности с известията" + diff --git a/libraries/troubleshoot/impl/src/main/res/values-hu/translations.xml b/libraries/troubleshoot/impl/src/main/res/values-hu/translations.xml index 6f359ecb22..ff76ef3a44 100644 --- a/libraries/troubleshoot/impl/src/main/res/values-hu/translations.xml +++ b/libraries/troubleshoot/impl/src/main/res/values-hu/translations.xml @@ -1,6 +1,6 @@ - "Leküldési értesítés előzmények" + "Leküldéses értesítések előzmények" "Tesztek futtatása" "Tesztek újbóli futtatása" "Egyes tesztek sikertelenek voltak. Ellenőrizze a részleteket." diff --git a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/DefaultPushHistoryEntryPointTest.kt b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/DefaultPushHistoryEntryPointTest.kt index 3604622f5c..858956488c 100644 --- a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/DefaultPushHistoryEntryPointTest.kt +++ b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/DefaultPushHistoryEntryPointTest.kt @@ -12,7 +12,7 @@ import com.bumble.appyx.core.modality.BuildContext import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.push.test.FakePushService import io.element.android.libraries.troubleshoot.api.PushHistoryEntryPoint import io.element.android.services.analytics.test.FakeScreenTracker @@ -32,15 +32,21 @@ class DefaultPushHistoryEntryPointTest { PushHistoryNode( buildContext = buildContext, plugins = plugins, - presenter = PushHistoryPresenter( - pushService = FakePushService(), - ), + presenterFactory = { + PushHistoryPresenter( + pushHistoryNavigator = object : PushHistoryNavigator { + override fun navigateTo(roomId: RoomId, eventId: EventId) = lambdaError() + }, + pushService = FakePushService(), + matrixClient = FakeMatrixClient(), + ) + }, screenTracker = FakeScreenTracker(), ) } val callback = object : PushHistoryEntryPoint.Callback { override fun onDone() = lambdaError() - override fun onItemClick(sessionId: SessionId, roomId: RoomId, eventId: EventId) = lambdaError() + override fun navigateTo(roomId: RoomId, eventId: EventId) = lambdaError() } val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) .callback(callback) 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 313735b6e6..9c1cdee25c 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 @@ -11,9 +11,19 @@ package io.element.android.libraries.troubleshoot.impl.history import com.google.common.truth.Truth.assertThat import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId +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.matrix.test.A_SESSION_ID_2 +import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.push.api.PushService import io.element.android.libraries.push.test.FakePushService +import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.test import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @@ -29,6 +39,7 @@ class PushHistoryPresenterTest { assertThat(initialState.pushHistoryItems).isEmpty() assertThat(initialState.showOnlyErrors).isFalse() assertThat(initialState.resetAction).isEqualTo(AsyncAction.Uninitialized) + assertThat(initialState.showNotSameAccountError).isFalse() } } @@ -119,11 +130,57 @@ class PushHistoryPresenterTest { } } + @Test + fun `present - item click current account`() = runTest { + val pushHistoryNavigatorResult = lambdaRecorder { _, _ -> } + val presenter = createPushHistoryPresenter( + pushHistoryNavigator = { roomId, eventId -> + pushHistoryNavigatorResult(roomId, eventId) + } + ) + presenter.test { + val initialState = awaitItem() + initialState.eventSink( + PushHistoryEvents.NavigateTo( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + ) + ) + pushHistoryNavigatorResult.assertions() + .isCalledOnce() + .with(value(A_ROOM_ID), value(AN_EVENT_ID)) + } + } + + @Test + fun `present - item click not current account`() = runTest { + val presenter = createPushHistoryPresenter() + presenter.test { + val initialState = awaitItem() + initialState.eventSink( + PushHistoryEvents.NavigateTo( + sessionId = A_SESSION_ID_2, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + ) + ) + assertThat(awaitItem().showNotSameAccountError).isTrue() + // Reset error + initialState.eventSink(PushHistoryEvents.ClearDialog) + assertThat(awaitItem().showNotSameAccountError).isFalse() + } + } + private fun createPushHistoryPresenter( + pushHistoryNavigator: PushHistoryNavigator = PushHistoryNavigator { _, _ -> lambdaError() }, pushService: PushService = FakePushService(), + matrixClient: MatrixClient = FakeMatrixClient(), ): PushHistoryPresenter { return PushHistoryPresenter( + pushHistoryNavigator = pushHistoryNavigator, pushService = pushService, + matrixClient = matrixClient, ) } } 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 5c98b2c21a..ada8b61f35 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 @@ -14,20 +14,14 @@ import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.element.android.libraries.matrix.api.core.EventId -import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_FORMATTED_DATE 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.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalled -import io.element.android.tests.testutils.EnsureNeverCalledWithThreeParams import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn -import io.element.android.tests.testutils.lambda.lambdaRecorder -import io.element.android.tests.testutils.lambda.value import org.junit.Rule import org.junit.Test import org.junit.rules.TestRule @@ -103,9 +97,8 @@ class PushHistoryViewTest { } @Test - fun `clicking on a valid event invokes the expected callback`() { - val eventsRecorder = EventsRecorder(expectEvents = false) - val onItemClick = lambdaRecorder { _, _, _ -> } + fun `clicking on a valid event emits the expected Event`() { + val eventsRecorder = EventsRecorder() rule.setPushHistoryView( aPushHistoryState( pushHistoryItems = listOf( @@ -118,25 +111,26 @@ class PushHistoryViewTest { ), eventSink = eventsRecorder, ), - onItemClick = onItemClick, ) rule.onNodeWithText(A_FORMATTED_DATE).performClick() - onItemClick.assertions() - .isCalledOnce() - .with(value(A_SESSION_ID), value(A_ROOM_ID), value(AN_EVENT_ID)) + eventsRecorder.assertSingle( + PushHistoryEvents.NavigateTo( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + ) + ) } } private fun AndroidComposeTestRule.setPushHistoryView( state: PushHistoryState, onBackClick: () -> Unit = EnsureNeverCalled(), - onItemClick: (SessionId, RoomId, EventId) -> Unit = EnsureNeverCalledWithThreeParams(), ) { setContent { PushHistoryView( state = state, onBackClick = onBackClick, - onItemClick = onItemClick, ) } } diff --git a/libraries/ui-common/build.gradle.kts b/libraries/ui-common/build.gradle.kts new file mode 100644 index 0000000000..55ef7eaf49 --- /dev/null +++ b/libraries/ui-common/build.gradle.kts @@ -0,0 +1,19 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +plugins { + id("io.element.android-compose-library") +} + +android { + namespace = "io.element.android.libraries.ui.common" +} + +dependencies { + implementation(libs.appyx.core) + implementation(projects.libraries.designsystem) +} diff --git a/libraries/ui-common/src/main/kotlin/io/element/android/libraries/ui/common/nodes/EmptyNode.kt b/libraries/ui-common/src/main/kotlin/io/element/android/libraries/ui/common/nodes/EmptyNode.kt new file mode 100644 index 0000000000..ab622b4b9d --- /dev/null +++ b/libraries/ui-common/src/main/kotlin/io/element/android/libraries/ui/common/nodes/EmptyNode.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2025 New Vector 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.ui.common.nodes + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +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.node.node +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight + +/** + * Ref: https://www.figma.com/design/0MMNu7cTOzLOlWb7ctTkv3/Element-X?node-id=1518-85323 + */ +fun emptyNode( + buildContext: BuildContext, +): Node = node(buildContext) { modifier -> + EmptyView(modifier) +} + +@Composable +private fun EmptyView( + modifier: Modifier = Modifier, +) = Box( + modifier = modifier + .fillMaxSize() + .background(ElementTheme.colors.bgCanvasDefault), +) + +@PreviewsDayNight +@Composable +internal fun EmptyViewPreview() = ElementPreview { + EmptyView(Modifier) +} diff --git a/libraries/ui-strings/src/main/res/values-be/translations.xml b/libraries/ui-strings/src/main/res/values-be/translations.xml index b964fff4c0..247dc9f026 100644 --- a/libraries/ui-strings/src/main/res/values-be/translations.xml +++ b/libraries/ui-strings/src/main/res/values-be/translations.xml @@ -326,4 +326,5 @@ "Месцазнаходжанне" "Версія: %1$s (%2$s)" "be" + "У вас няма доступу да гэтага паведамлення" diff --git a/libraries/ui-strings/src/main/res/values-bg/translations.xml b/libraries/ui-strings/src/main/res/values-bg/translations.xml index 153476931b..57b3a43d1a 100644 --- a/libraries/ui-strings/src/main/res/values-bg/translations.xml +++ b/libraries/ui-strings/src/main/res/values-bg/translations.xml @@ -1,14 +1,21 @@ + "Добавяне на реакция: %1$s" "Изтриване" "%1$d въведена цифра" "%1$d въведени цифри" + "Пълният адрес ще бъде %1$s" + "Подробности за шифроването" "Скриване на паролата" + "Присъединяване към обаждане" "Скок към най-долу" "Само споменавания" "Заглушено" + "Нови споменавания" + "Нови съобщения" + "Текущо обаждане" "Страница %1$d" "Пауза" "PIN поле" @@ -26,16 +33,20 @@ "Премахване на реакция с %1$s" "Изпращане на файлове" "Показване на паролата" + "Започнете обаждане" "Потребителско меню" "Вижте подробности" "Приемане" + "Добавяне към хронологията" "Назад" + "Обаждане" "Отказ" "Избор на снимка" "Изчистване" "Затваряне" "Завършване на потвърждаването" "Потвърждаване" + "Потвърдете паролата" "Продължаване" "Копиране" "Копиране на връзката" @@ -68,6 +79,7 @@ "Напускане" "Напускане на разговора" "Напускане на стаята" + "Напускане на пространството" "Зареждане на още" "Управление на профила" "Управление на устройствата" @@ -85,7 +97,11 @@ "Премахване на съобщението" "Отговор" "Отговор в нишка" + "Докладване" + "Докладване на грешка" "Докладване на съдържанието" + "Докладване на стаята" + "Нулиране" "Повторен опит" "Повторен опит за разшифроване" "Запазване" @@ -102,25 +118,35 @@ "Започване" "Започване на чат" "Започване на потвърждаването" + "Докоснете за зареждане на карта" "Снимка" "Докоснете за опции" "Повторен опит" + "Откачване" "Преглед на източника" "Да" "Да, опитай отново" "Относно" + "Политика за приемлива употреба" + "Добавяне на акаунт" "Разширени настройки" "Статистика" + "Напуснахте стаята" "Облик" "Аудио" "Блокирани потребители" + "Мехурчета" + "Започнато обаждане" "Резервно копие на чатовете" + "Авторски права" "Създаване на стая…" "Тъмен" "Грешка при разшифроване" + "Описание" "Опции за разработчици" "Директен чат" "Не показвай това отново" + "Неуспешно изтегляне" "Изтегля се" "(редактирано)" "Редактиране" @@ -132,18 +158,22 @@ "Грешка" "Всеки" "Фаворизиране" + "Фаворизирано" "Файл" "Файлът е изтрит" "Файлът е запазен" + "Файлът е запазен в Изтеглени" "Препращане на съобщението" "GIF" "Изображение" "В отговор на %1$s" "Инсталиране на APK" + "Този Matrix ID не може да бъде намерен, така че поканата може да не бъде получена." "Стаята се напуска" "Светъл" "Връзката е копирана в клипборда" "Зарежда се…" + "Зарежда се още…" "%d друг" "%d други" @@ -158,13 +188,18 @@ "Модерно" "Заглушаване" "Няма резултати" + "Няма име на стая" + "Няма име на пространство" "Без шифроване" "Офлайн" + "Лицензи за отворен код" "или" "Парола" "Хора" "Постоянна връзка" "Разрешение" + "Закачено" + "Моля, проверете вашата интернет връзка" "Моля, изчакайте…" "Сигурни ли сте, че искате да приключите тази анкета?" "Анкета: %1$s" @@ -174,19 +209,32 @@ "%d глас" "%d гласа" + "Подготвя се…" "Политика за поверителност" "Частна стая" "Общодостъпна стая" + "Общодостъпно пространство" "Реакция" "Реакции" "Причина" "Ключ за възстановяване" + "Опреснява се…" + + "%1$d отговор" + "%1$d отговора" + + "В отговор на %1$s" "Съобщаване за грешка" "Съобщаване за проблем" + "Докладът е изпратен" "Редактор на богат текст" "Стая" "Име на стаята" "напр. името на вашия проект" + + "%1$d стая" + "%1$d стаи" + "Запазва се" "Заключване на екрана" "Търсене на някого" @@ -197,20 +245,28 @@ "Изпращането е неуспешно" "Изпратено" "Сървърът не се поддържа" + "URL адрес на сървъра" "Настройки" "Споделено местоположение" "Излизате" "Възникна проблем. Моля, опитайте отново." + + "%1$d пространство" + "%1$d пространства" + "Започване на чат…" + "Стикер" "Успешно" "Предложения" "Синхронизиране" "Система" "Текст" + "Уведомления от трети страни" "Нишка" "Тема" "За какво се отнася тази стая?" "Не може да се разшифрова" + "Поканите не можаха да бъдат изпратени до един или повече потребители." "Не може да се изпрати покана(и)" "Отключване" "Раззаглушаване" @@ -225,16 +281,35 @@ "Потвърждаване на потребителя" "Видео" "Гласово съобщение" + "Изчаква се…" "В очакване на това съобщение" "Вие" + "Потвърждение" "Грешка" "Успешно" "Внимание" + "Неуспешно създаване на постоянна връзка" + "%1$s не успя да зареди картата. Моля, опитайте отново по-късно." + "Неуспешно зареждане на съобщения" + "%1$s няма достъп до вашето местоположение. Моля, опитайте отново по-късно." + "%1$s няма разрешение за достъп до вашето местоположение. Можете да активирате достъпа в Настройки." + "%1$s няма разрешение за достъп до вашето местоположение. Активирайте достъпа по-долу." + "Някои съобщения не са изпратени" + "Съжаляваме, възникна грешка" "Без шифроване" "🔐️ Присъединете се към мен в %1$s" "Хей, говорете с мен в %1$s: %2$s" "%1$s Android" + "Неуспешен избор на мултимедия, моля, опитайте отново." + "Натиснете върху съобщение и изберете „%1$s“, за да го включите тук." + "Закачете важни съобщения, за да могат лесно да бъдат намерени" + + "%1$d закачено съобщение" + "%1$d закачени съобщения" + "Закачени съобщения" + "Неуспешна обработка на мултимедия за качване, моля, опитайте отново." + "Не могат да бъдат извлечени потребителските данни" "Споделяне на местоположение" "Споделяне на моето местоположение" "Отваряне в Apple Maps" diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index f3aa72c290..f0e10894b4 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -82,6 +82,7 @@ "Odmítnout" "Odmítnout a zablokovat" "Odstranit hlasování" + "Odznačit vše" "Zakázat" "Vyřadit" "Zavřít" @@ -140,6 +141,7 @@ "Opakovat dešifrování" "Uložit" "Hledat" + "Vybrat vše" "Odeslat" "Odeslat upravenou zprávu" "Odeslat zprávu" @@ -250,6 +252,7 @@ Důvod: %1$s." "%1$s (%2$s)" "Žádné výsledky" "Žádný název místnosti" + "Žádný název prostoru" "Nešifrováno" "Offline" "Licence s otevřeným zdrojovým kódem" @@ -419,9 +422,6 @@ Opravdu chcete pokračovat?" "Ahoj, ozvi se mi na %1$s: %2$s" "%1$s Android" "Zatřeste zařízením pro nahlášení chyby" - "Tím budete také odstraněni ze všech místností v tomto prostoru." - "Tímto budete také odstraněni ze všech místností v tomto prostoru, včetně těch, jejichž jediným správcem jste:" - "Opustit %1$s?" "Snímek obrazovky" "%1$s: %2$s" "Možnosti" @@ -466,6 +466,7 @@ Opravdu chcete pokračovat?" "Sdílet tuto polohu" "Prostory, které jste vytvořili nebo se k nim připojili." "%1$s • %2$s" + "%1$s prostor" "Prostory" "Zpráva nebyla odeslána, protože ověřená identita uživatele %1$s se změnila." "Zpráva nebyla odeslána, protože%1$s neověřil(a) všechna zařízení." diff --git a/libraries/ui-strings/src/main/res/values-cy/translations.xml b/libraries/ui-strings/src/main/res/values-cy/translations.xml index a709e010d9..11b46cb85c 100644 --- a/libraries/ui-strings/src/main/res/values-cy/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cy/translations.xml @@ -88,6 +88,7 @@ "Gwrthod" "Gwrthod a rhwystro" "Dileu Pleidlais" + "Dad ddewis y cyfan" "Analluogi" "Dileu" "Cau" @@ -146,6 +147,7 @@ "Cynnig arall ar ddadgryptio" "Cadw" "Chwilio" + "Dewis y cyfan" "Anfon" "Anfon neges wedi\'i golygu" "Anfonwch neges" @@ -262,6 +264,7 @@ Rheswm: %1$s." "%1$s (%2$s)" "Dim canlyniadau" "Dim enw i\'r ystafell" + "Dim enw gofod" "Heb ei amgryptio" "All-lein" "Trwyddedau cod agored" @@ -402,8 +405,8 @@ Rheswm: %1$s." Ydych chi\'n siŵr eich bod am barhau?" "Gwnewch yn siŵr fod y ddolen hon yn iawn" "Dewiswch ansawdd rhagosodedig y fideos rydych chi\'n eu llwytho." - "Ansawdd llwytho fideo" - "Y maint ffeil mwyaf sy\'n cael ei ganiatáu yw:%1$s" + "Ansawdd lwytho fideo" + "Y maint ffeil mwyaf sy\'n cael ei ganiatáu yw: %1$s" "Mae maint y ffeil yn rhy fawr i\'w llwytho" "Adroddwyd am yr ystafell" "Adroddwyd a gadael yr ystafell" @@ -445,9 +448,6 @@ Ydych chi\'n siŵr eich bod am barhau?" "Hei, siaradwch â mi ar %1$s: %2$s" "Android %1$s" "Rageshake i adrodd gwall" - "Bydd hyn hefyd yn eich tynnu o bob ystafell yn y gofod hwn." - "Bydd hyn hefyd yn eich tynnu o bob ystafell yn y gofod hwn, gan gynnwys y rhai rydych chi\'n unig weinyddwr ar eu cyfer:" - "Gadael %1$s ?" "Llun sgrin" "%1$s: %2$s" "Dewisiadau" @@ -495,6 +495,7 @@ Ydych chi\'n siŵr eich bod am barhau?" "Rhannu\'r lleoliad hwn" "Gofodau rydych wedi\'u creu neu wedi ymuno â nhw." "%1$s • %2$s" + "Gofod %1$s" "Gofodau" "Heb anfon y neges oherwydd bod hunaniaeth wedi \'i ddilysu %1$s wedi\'i ailosod." "Heb anfon y neges oherwydd nid yw %1$s wedi gwirio pob dyfais." diff --git a/libraries/ui-strings/src/main/res/values-da/translations.xml b/libraries/ui-strings/src/main/res/values-da/translations.xml index 44f0321315..e133ec28ac 100644 --- a/libraries/ui-strings/src/main/res/values-da/translations.xml +++ b/libraries/ui-strings/src/main/res/values-da/translations.xml @@ -80,6 +80,7 @@ "Afvis" "Afvis og blokér" "Slet afstemning" + "Fravælg alle" "Deaktiver" "Kassér" "Afvis" @@ -94,6 +95,7 @@ "Har du glemt din adgangskode?" "Videresend" "Gå tilbage" + "Gå til indstillinger" "Ignorér" "Invitér" "Invitér andre" @@ -105,10 +107,12 @@ "Forlad" "Forlad samtalen" "Forlad rum" + "Forlad klynge" "Indlæs mere" "Administrer konto" "Administrer enheder" "Besked" + "Minimér" "Næste" "Nej" "Ikke nu" @@ -137,6 +141,7 @@ "Prøv at dekryptere igen" "Gem" "Søg" + "Vælg alle" "Send" "Send redigeret besked" "Send besked" @@ -175,6 +180,7 @@ "Du blev logget ud af sessionen" "Udseende" "Lyd" + "Beta" "Blokerede brugere" "Bobler" "Opkald startet" @@ -184,6 +190,7 @@ "Opretter rum…" "Anmodning annulleret" "Forlod rummet" + "Forlod klynge" "Invitationen blev afvist" "Mørkt tema" "Fejl under dekryptering" @@ -222,6 +229,7 @@ "Installer APK" "Dette Matrix-ID kan ikke findes, så invitationen modtages muligvis ikke." "Forlader rummet" + "Forlader klynge" "Lyst tema" "Linje kopieret til udklipsholder" "Linket er kopieret til udklipsholderen" @@ -244,6 +252,7 @@ "%1$s (%2$s)" "Ingen resultater" "Intet rumnavn" + "Intet klyngenavn" "Ikke krypteret" "Offline" "Open Source-licenser" @@ -306,7 +315,9 @@ "Serveren er ikke tilgængelig" "Server URL" "Indstillinger" + "Del klynge" "Delt placering" + "Delt klynge" "Logger ud" "Noget gik galt" "Vi stødte på et problem. Prøv venligst igen." @@ -452,6 +463,7 @@ Er du sikker på, at du vil fortsætte?" "Del denne lokation" "Klynger, du har oprettet eller deltager i" "%1$s•%2$s" + "%1$s klynge" "Klynger" "Beskeden blev ikke sendt fordi %1$s s bekræftede identitet blev nulstillet." "Meddelelsen er ikke sendt, fordi %1$s ikke har bekræftet alle enheder." @@ -461,7 +473,7 @@ Er du sikker på, at du vil fortsætte?" "da" "Historiske beskeder er ikke tilgængelige på denne enhed" "Du skal verificere denne enhed for at få adgang til historiske beskeder" - "Du har ikke adgang til denne besked" + "Du har ikke adgang til denne meddelelse" "Kan ikke dekryptere beskeden" "Denne besked blev blokeret, enten fordi du ikke verificerede din enhed, eller fordi afsenderen skal have verificeret din identitet." diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index 39396928eb..d1c8b7a6f4 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -80,6 +80,7 @@ "Ablehnen" "Ablehnen und blockieren" "Umfrage löschen" + "Auswahl aufheben" "Deaktivieren" "Verwerfen" "Schließen" @@ -94,6 +95,7 @@ "Passwort vergessen?" "Weiterleiten" "Zurück" + "Zu den Einstellungen" "Ignorieren" "Einladen" "Nutzer einladen" @@ -110,6 +112,7 @@ "Konto verwalten" "Geräte verwalten" "Nachricht" + "Minimieren" "Weiter" "Nein" "Später" @@ -138,6 +141,7 @@ "Entschlüsselung wiederholen" "Speichern" "Suchen" + "Alles auswählen" "Senden" "Bearbeitete Nachricht senden" "Nachricht senden" @@ -176,6 +180,7 @@ "Du wurdest aus der Sitzung abgemeldet." "Erscheinungsbild" "Audio" + "Beta" "Blockierte Nutzer" "Sprechblasen" "Anruf gestartet" @@ -224,6 +229,7 @@ Grund: %1$s." "APK installieren" "Diese Matrix Kennung wurde nicht gefunden, daher wird die Einladung möglicherweise nicht empfangen." "Chat verlassen" + "Space wird verlassen" "Hell" "Zeile in die Zwischenablage kopiert" "Link in die Zwischenablage kopiert" @@ -246,6 +252,7 @@ Grund: %1$s." "%1$s(%2$s)" "Keine Ergebnisse" "Kein Chat-Name" + "Kein Space Name" "Nicht verschlüsselt" "Offline" "Open-Source-Lizenzen" @@ -309,7 +316,9 @@ Grund: %1$s." "Server nicht erreichbar" "Server-URL" "Einstellungen" + "Space teilen" "Geteilter Standort" + "Gemeinsamer Space" "Abmelden" "Es ist ein Fehler aufgetreten." "Wir haben ein Problem festgestellt. Bitte versuch es erneut." @@ -412,9 +421,6 @@ Möchtest du wirklich fortfahren?" "Hey, sprich mit mir auf %1$s: %2$s" "%1$s Android" "Heftiges Schütteln um Fehler zu melden" - "Dadurch wirst du auch aus allen Chats in diesem Space entfernt." - "Dadurch wirst du auch aus allen Chats in diesem Space entfernt, auch aus denen, für die du der einzige Admin bist:" - "%1$s verlassen?" "Bildschirmfoto" "%1$s: %2$s" "Optionen" @@ -458,6 +464,7 @@ Möchtest du wirklich fortfahren?" "Diesen Standort teilen" "Von dir erstellte oder beigetretene Spaces." "%1$s • %2$s" + "%1$s Space" "Spaces" "Nachricht nicht gesendet, weil sich die verifizierte Identität von %1$s geändert hat." "Die Nachricht wurde nicht gesendet, weil %1$s nicht alle Geräte verifiziert hat." diff --git a/libraries/ui-strings/src/main/res/values-en-rUS/translations.xml b/libraries/ui-strings/src/main/res/values-en-rUS/translations.xml index 05bce25dfd..00d2674ed7 100644 --- a/libraries/ui-strings/src/main/res/values-en-rUS/translations.xml +++ b/libraries/ui-strings/src/main/res/values-en-rUS/translations.xml @@ -1,5 +1,6 @@ + "Minimize" "Favorite" "Favorited" diff --git a/libraries/ui-strings/src/main/res/values-eo/translations.xml b/libraries/ui-strings/src/main/res/values-eo/translations.xml index 3368f36f08..6f796aef09 100644 --- a/libraries/ui-strings/src/main/res/values-eo/translations.xml +++ b/libraries/ui-strings/src/main/res/values-eo/translations.xml @@ -1,6 +1,6 @@ - "Manage connected devices" + "Manage linked devices" "Start fresh" "Backup password" "Sent from an unconfirmed device" @@ -16,11 +16,11 @@ "Your message was not sent because %1$s\'s security verification has changed" "%1$s is using one or more unconfirmed devices. You can send the message anyway, or you can cancel for now and try again later after %2$s has confirmed all their devices." "Your message was not sent because %1$s has not confirmed all devices" - "One or more of your connected devices are unconfirmed. You can send the message anyway, or you can cancel for now and try again later after you have confirmed all of your connected devices." - "Your message was not sent because you have not confirmed one or more of your connected devices" + "One or more of your linked devices are unconfirmed. You can send the message anyway, or you can cancel for now and try again later after you have confirmed all of your linked devices." + "Your message was not sent because you have not confirmed one or more of your linked devices" "Message not sent because %1$s\'s security verification has changed." "Message not sent because %1$s has not confirmed all their devices." - "Message not sent because you have not confirmed one or more of your connected devices." + "Message not sent because you have not confirmed one or more of your linked devices." "You need to confirm this device for access to historical messages" "This message was blocked either because you did not confirm your device or because the sender needs to verify you." diff --git a/libraries/ui-strings/src/main/res/values-et/translations.xml b/libraries/ui-strings/src/main/res/values-et/translations.xml index e02bba3558..dd6bae100e 100644 --- a/libraries/ui-strings/src/main/res/values-et/translations.xml +++ b/libraries/ui-strings/src/main/res/values-et/translations.xml @@ -80,6 +80,7 @@ "Keeldu" "Keeldu ja blokeeri" "Kustuta küsitlus" + "Eemalda kõik valikud" "Lülita välja" "Loobu" "Lõpeta" @@ -110,6 +111,7 @@ "Halda kasutajakontot" "Halda seadmeid" "Saada sõnum" + "Minimeeri" "Edasi" "Ei" "Mitte praegu" @@ -138,6 +140,7 @@ "Proovi dekrüptimist uuesti" "Salvesta" "Otsi" + "Vali kõik" "Saada" "Saada muudetud sõnum" "Saada sõnum" @@ -185,6 +188,7 @@ "Loome jututoa…" "Päring on tühistatud" "Lahkus jututoast" + "Lahkus kogukonnast" "Keeldusid kutsest" "Tume" "Dekrüptimisviga" @@ -245,6 +249,7 @@ Põhjus: %1$s." "%1$s (%2$s)" "Otsingul pole tulemusi" "Jututoal puudub nimi" + "Kogukonnal pole nime" "Krüptimata" "Võrgust väljas" "Avatud lähtekoodiga litsentsid" @@ -308,6 +313,7 @@ Põhjus: %1$s." "Server pole leitav" "Serveri URL" "Seadistused" + "Jaga kogukonda" "Jagatud asukoht" "Logime välja" "Midagi läks valesti" @@ -368,7 +374,7 @@ Põhjus: %1$s." Kas sa oled kindel, et soovid jätkata?" "Palun kontrolli seda linki mõttega" "Vali üleslaaditavate videote kvaliteeditase" - "Videote kvaliteeditase" + "Üleslaaditavate videote kvaliteet" "Suurim lubatud failisuurus on: %1$s" "See fail on üleslaadimiseks liiga suur" "Teatasid jututoast" @@ -411,9 +417,6 @@ Kas sa oled kindel, et soovid jätkata?" "Hei, suhtle minuga %1$s võrgus: %2$s" "%1$s Android" "Veast teatamiseks raputa nutiseadet ägedalt" - "Sellega eemaldad end ka kõikidest antud kogukonna jututubadest." - "Sellega eemaldad end ka kõikidest antud kogukonna jututubadest, sealhulgast järgnevaist, kus oled ainus peakasutaja:" - "Kas lahkud %1$s kogukonnast?" "Ekraanitõmmis" "%1$s: %2$s" "Valikud" @@ -457,6 +460,7 @@ Kas sa oled kindel, et soovid jätkata?" "Jaga seda asukohta" "Sinu loodud kogukonnad ning need, millega oled liitunud." "%1$s • %2$s" + "Kogukond: %1$s" "Kogukonnad" "Sõnum on saatmata, kuna kasutaja %1$s verifitseeritud identiteet on lähtestatud." "Sõnum on saatmata, kuna %1$s pole verifitseerinud kõiki oma seadmeid." @@ -466,7 +470,7 @@ Kas sa oled kindel, et soovid jätkata?" "et" "Vanu sõnumeid ei saa selles seadmes näha" "Ligipääsuks vanadele sõnumitele pead selle seadme verifitseerima" - "Sul puudub ligipääs sellele sõnumile" + "Sul pole ligipääsu antud sõnumile" "Sõnumi dekrüptimine ei õnnestu" "Kuna seade on verifitseerimata või saatja pole sind verifitseerinud, siis sõnumi näitamine on blokeeritud." diff --git a/libraries/ui-strings/src/main/res/values-eu/translations.xml b/libraries/ui-strings/src/main/res/values-eu/translations.xml index 28f8abc98e..15bfa21fb5 100644 --- a/libraries/ui-strings/src/main/res/values-eu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-eu/translations.xml @@ -328,8 +328,7 @@ Arrazoia: %1$s." Ziur jarraitu nahi duzula?" "Egiaztatu honako esteka" - "Bideoaren igoera-kalitatea" - "Onartutako fitxategiaren gehienezko tamaina %1$s da" + "Bideoen igoera-kalitatea" "Fitxategiaren tamaina handiegia da igotzeko" "Gela salatu da" "Gela salatu eta utzi da" diff --git a/libraries/ui-strings/src/main/res/values-fi/translations.xml b/libraries/ui-strings/src/main/res/values-fi/translations.xml index 326d67c2d9..5959066e9b 100644 --- a/libraries/ui-strings/src/main/res/values-fi/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fi/translations.xml @@ -80,6 +80,7 @@ "Hylkää" "Hylkää ja estä" "Poista kysely" + "Poista kaikki valinnat" "Poista käytöstä" "Hylkää" "Sulje" @@ -94,6 +95,7 @@ "Unohditko salasanan?" "Välitä" "Takaisin" + "Siirry asetuksiin" "Ohita" "Kutsu" "Kutsu ihmisiä" @@ -110,6 +112,7 @@ "Hallitse tiliä" "Hallitse laitteita" "Lähetä viesti" + "Pienennä" "Seuraava" "Ei" "Ei nyt" @@ -138,6 +141,7 @@ "Yritä salauksen purkamista uudelleen" "Tallenna" "Hae" + "Valitse kaikki" "Lähetä" "Lähetä muokattu viesti" "Lähetä viesti" @@ -176,6 +180,7 @@ "Sinut kirjattiin ulos istunnosta" "Ulkoasu" "Ääni" + "Beeta" "Estetyt käyttäjät" "Kuplat" "Puhelu alkoi" @@ -224,6 +229,7 @@ Syy: %1$s." "Asenna APK" "Tätä Matrix-tunnusta ei löytynyt, joten kutsu ei välttämättä mene perille." "Poistutaan huoneesta" + "Poistutaan tilasta" "Vaalea" "Rivi kopioitu leikepöydälle" "Linkki kopioitu leikepöydälle" @@ -246,6 +252,7 @@ Syy: %1$s." "%1$s (%2$s)" "Ei tuloksia" "Nimetön huone" + "Nimetön tila" "Ei salattu" "Ei yhteyttä" "Avoimen lähdekoodin lisenssit" @@ -311,6 +318,7 @@ Syy: %1$s." "Asetukset" "Jaa tila" "Jaettu sijainti" + "Jaettu tila" "Kirjaudutaan ulos" "Jokin meni pieleen" "Kohtasimme ongelman. Yritä uudelleen." @@ -370,7 +378,7 @@ Syy: %1$s." Haluatko varmasti jatkaa?" "Tarkista tämä linkki" "Valitse lähettämäsi videoiden oletuslaatu." - "Videoiden lähetyslaatu" + "Videon lähetyslaatu" "Suurin sallittu tiedostokoko on: %1$s" "Tiedostokoko on liian suuri lähetettäväksi" "Huone ilmoitettu" @@ -413,9 +421,6 @@ Haluatko varmasti jatkaa?" "Hei, keskustele kanssani %1$s -sovelluksessa: %2$s" "%1$s Android" "Raivostunut ravistaminen ilmoittaa virheestä" - "Tämä poistaa sinut myös kaikista tämän tilan huoneista." - "Tämä poistaa sinut myös kaikista tämän tilan huoneista, mukaan lukien ne, joissa olet ainoa ylläpitäjä:" - "Haluatko poistua tilasta %1$s?" "Näyttökuva" "%1$s: %2$s" "Vaihtoehdot" @@ -459,6 +464,7 @@ Haluatko varmasti jatkaa?" "Jaa tämä sijainti" "Luomasi tai liittymäsi tilat." "%1$s • %2$s" + "%1$s tila" "Tilat" "Viestiä ei lähetetty, koska käyttäjän %1$s vahvistettu identiteetti nollattiin." "Viestiä ei lähetetty, koska %1$s ei ole vahvistanut kaikkia laitteitaan." diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml index 506a0936d9..591795aaee 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -80,6 +80,7 @@ "Refuser" "Refuser et bloquer" "Supprimer le sondage" + "Tout désélectionner" "Désactiver" "Annuler" "Ignorer" @@ -94,6 +95,7 @@ "Mot de passe oublié ?" "Transférer" "Retour" + "Ouvrir les paramètres" "Ignorer" "Inviter" "Inviter des amis" @@ -105,10 +107,12 @@ "Quitter" "Quitter la discussion" "Quitter le salon" + "Quitter l’espace" "Voir plus" "Gérer le compte" "Gérez les sessions" "Message" + "Minimiser" "Suivant" "Non" "Pas maintenant" @@ -137,6 +141,7 @@ "Réessayer le déchiffrement" "Enregistrer" "Rechercher" + "Tout sélectionner" "Envoyer" "Envoyer les modifications" "Envoyer un message" @@ -175,6 +180,7 @@ "Vous avez été déconnecté de la session" "Apparence" "Audio" + "Bêta" "Utilisateurs bloqués" "Bulles" "Appel démarré" @@ -183,7 +189,8 @@ "Droits d’auteur" "Création du salon…" "Demande annulée" - "Quitter le salon" + "Vous avez quitté le salon" + "Vous avez quitté l’espace" "Invitation refusée" "Sombre" "Erreur de déchiffrement" @@ -222,6 +229,7 @@ Raison : %1$s." "Installer l’APK" "Cet identifiant Matrix est introuvable, il est donc possible que l’invitation ne soit pas reçue." "Quitter le salon…" + "En train de quitter l’espace" "Clair" "Ligne copiée dans le presse-papiers" "Lien copié dans le presse-papiers" @@ -244,6 +252,7 @@ Raison : %1$s." "%1$s (%2$s)" "Aucun résultat" "Salon sans nom" + "Espace sans nom" "Non chiffré" "Hors ligne" "Licences open source" @@ -307,7 +316,9 @@ Raison : %1$s." "Serveur inaccessible" "URL du serveur" "Paramètres" + "Partager l’espace" "Position partagée" + "Espace partagé" "Déconnexion" "Une erreur s’est produite" "Nous avons rencontré un problème. Veuillez réessayer." @@ -453,6 +464,7 @@ Raison : %1$s." "Partager cette position" "Espaces que vous avez créés ou rejoints." "%1$s • %2$s" + "Espace %1$s" "Espaces" "Le message n’a pas été envoyé car l’identité vérifiée de %1$s a été réinitialisée." "Le message n’a pas été envoyé car %1$s n’a pas vérifié tous ses appareils." @@ -462,7 +474,7 @@ Raison : %1$s." "fr" "Les anciens messages ne sont pas disponibles sur cet appareil" "Vous devez vérifier cet appareil pour accéder à l’historique des messages" - "Vous n’avez pas accès à ce message" + "Vous ne pouvez pas voir ce message" "Impossible de déchiffrer le message" "Ce message a été bloqué soit parce que vous n’avez pas vérifié votre session, soit parce que l’expéditeur doit vérifier votre identité." diff --git a/libraries/ui-strings/src/main/res/values-hu/translations.xml b/libraries/ui-strings/src/main/res/values-hu/translations.xml index 1cba6eccd2..40afdf7c1d 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -42,7 +42,7 @@ "Reakció eltávolítása: %1$s" "Szoba profilképe" "Fájlküldés" - "Időkorlátos művelet szükséges" + "Időkorlátos művelet szükséges, egy perce van az ellenőrzésre" "Jelszó megjelenítése" "Hanghívás indítása" "Elévült szoba" @@ -71,7 +71,7 @@ "Másolás" "Felirat másolása" "Hivatkozás másolása" - "Üzenetre mutató hivatkozás másolása" + "Üzenethivatkozás másolása" "Szöveg másolása" "Létrehozás" "Szoba létrehozása" @@ -80,6 +80,7 @@ "Elutasítás" "Elutasítás és letiltás" "Szavazás törlése" + "Kijelölés megszüntetése" "Letiltás" "Elvetés" "Eltüntetés" @@ -105,6 +106,7 @@ "Elhagyás" "Beszélgetés elhagyása" "Szoba elhagyása" + "Tér elhagyása" "Továbbiak betöltése" "Fiók kezelése" "Eszközök kezelése" @@ -137,6 +139,7 @@ "Visszafejtés újbóli megpróbálása" "Mentés" "Keresés" + "Összes kijelölése" "Küldés" "Szerkesztett üzenet küldése" "Üzenet küldése" @@ -165,6 +168,8 @@ "Frissítés érhető el" "Névjegy" "Elfogadható használatra vonatkozó szabályzat" + "Fiók hozzáadása" + "Másik fiók hozzáadása" "Felirat hozzáadása" "Speciális beállítások" "egy kép" @@ -182,9 +187,11 @@ "Szoba létrehozása…" "Kérés megszakítva" "Elhagyta a szobát" + "Tér elhagyva" "Meghívás elutasítva" "Sötét" "Visszafejtési hiba" + "Leírás" "Fejlesztői beállítások" "Eszközazonosító" "Közvetlen csevegés" @@ -241,6 +248,7 @@ Ok: %1$s." "%1$s (%2$s)" "Nincs találat" "Nincs szobanév" + "Nincs térnév" "Nincs titkosítva" "Kapcsolat nélkül" "Nyílt forráskódú licencek" @@ -293,14 +301,17 @@ Ok: %1$s." "Keresési találatok" "Biztonság" "Látta" + "Fiók kiválasztása" "Címzett" "Küldés…" "A küldés sikertelen" "Elküldve" ". " "A kiszolgáló nem támogatott" + "A kiszolgáló nem érhető el" "Kiszolgáló webcíme" "Beállítások" + "Tér megosztása" "Megosztott tartózkodási hely" "Kijelentkezés" "Valamilyen hiba történt" @@ -375,6 +386,8 @@ Biztos, hogy folytatja?" "A legnagyobb megengedett fájlméret: %1$s" "Válassza ki a feltöltendő videó minőségét." "Feltöltött videó minőségének kiválasztása" + "Emodzsik keresése" + "Már bejelentkezett erre az eszközre, mint %1$s." "A Matrix-kiszolgálót frissíteni kell a Matrix Authentication Service és a fióklétrehozás támogatásához." "Nem sikerült létrehozni az állandó hivatkozást" "Az %1$s nem tudta betölteni a térképet. Próbálja meg újra később." @@ -432,7 +445,7 @@ Biztos, hogy folytatja?" "Kibontás" "Csökkentés" "Már ezt a szobát nézi!" - "%1$s / %2$s" + "%1$s. / %2$s" "%1$s kitűzött üzenet" "Üzenet betöltése…" "Összes megtekintése" @@ -445,6 +458,7 @@ Biztos, hogy folytatja?" "E hely megosztása" "Létrehozott vagy olyan terek, melyekhez csatlakozott." "%1$s • %2$s" + "%1$s tér" "Terek" "Az üzenet nem lett elküldve, mert %1$s ellenőrzött személyazonossága megváltozott." "Az üzenet nem lett elküldve, mert %1$s nem ellenőrizte az összes eszközét." diff --git a/libraries/ui-strings/src/main/res/values-ko/translations.xml b/libraries/ui-strings/src/main/res/values-ko/translations.xml index 5d9dcbe2f2..256e561db5 100644 --- a/libraries/ui-strings/src/main/res/values-ko/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ko/translations.xml @@ -448,7 +448,7 @@ "ko" "이 장치에서는 과거 메시지를 사용할 수 없습니다." "이전 메시지에 액세스하려면 이 장치를 확인해야 합니다." - "이 메시지에 액세스할 수 없습니다." + "이 메시지에 액세스할 수 없습니다" "메시지를 해독할 수 없습니다." "이 메시지는 귀하가 기기를 확인하지 않았거나 발신자가 귀하의 신원을 확인해야 하기 때문에 차단되었습니다." diff --git a/libraries/ui-strings/src/main/res/values-nb/translations.xml b/libraries/ui-strings/src/main/res/values-nb/translations.xml index 24594c782e..6b0db984ea 100644 --- a/libraries/ui-strings/src/main/res/values-nb/translations.xml +++ b/libraries/ui-strings/src/main/res/values-nb/translations.xml @@ -2,6 +2,7 @@ "Legg til reaksjon: %1$s" "Profilbilde" + "Minimer meldingstekstfeltet" "Slett" "%1$d siffer angitt" @@ -10,6 +11,7 @@ "Rediger avatar" "Den fullstendige adressen vil være %1$s" "Krypteringsdetaljer" + "Utvid meldingstekstfeltet" "Skjul passord" "Bli med i samtale" "Hopp til bunnen" @@ -77,6 +79,7 @@ "Avslå" "Avslå og blokker" "Slett avstemning" + "Velg bort alle" "Deaktiver" "Forkast" "Avvis" @@ -87,9 +90,11 @@ "Aktiver" "Avslutt avstemning" "Skriv inn PIN-koden" + "Fullfør" "Glemt passordet?" "Videresend" "Gå tilbake" + "Gå til innstillinger" "Ignorer" "Inviter" "Inviter folk" @@ -101,10 +106,12 @@ "Forlat" "Forlat samtalen" "Forlat rommet" + "Forlat område" "Last inn mer" "Administrer konto" "Administrer enheter" "Melding" + "Minimer" "Neste" "Nei" "Ikke nå" @@ -133,6 +140,7 @@ "Prøv dekryptering på nytt" "Lagre" "Søke" + "Velg alle" "Sende" "Send redigert melding" "Send melding" @@ -161,12 +169,17 @@ "Oppgradering tilgjengelig" "Om" "Retningslinjer for akseptabel bruk" + "Legg til en konto" + "Legg til en annen konto" "Legger til bildetekst" "Avanserte innstillinger" "et bilde" "Analyse" + "Du forlot rommet" + "Du ble logget ut av økten" "Utseende" "Lyd" + "Beta" "Blokkerte brukere" "Bobler" "Samtale startet" @@ -176,9 +189,11 @@ "Oppretter rom …" "Forespørsel kansellert" "Forlot rommet" + "Forlot område" "Invitasjon avslått" "Mørk" "Dekrypteringsfeil" + "Beskrivelse" "Alternativer for utviklere" "Enhets-ID" "Direkte chat" @@ -213,6 +228,7 @@ "Installer APK" "Finner ikke denne Matrix-IDen, så invitasjonen blir kanskje ikke mottatt." "Forlater rommet" + "Forlater området" "Lys" "Linje kopiert til utklippstavlen" "Lenke kopiert til utklippstavlen" @@ -287,17 +303,22 @@ "Søkeresultater" "Sikkerhet" "Sett av" + "Velg en konto" "Sendt til" "Sender…" "Kunne ikke sende" "Sendt" "Server støttes ikke" + "Serveren er ikke tilgjengelig" "URL-adresse til server" "Innstillinger" + "Del område" "Delt posisjon" + "Delt område" "Logger av" "Noe gikk galt" "Vi har støtt på et problem. Vennligst prøv igjen." + "Område" "%1$d Område" "%1$d Områder" @@ -332,6 +353,12 @@ "Verifiser identiteten" "Verifiser bruker" "Video" + "Høy kvalitet" + "Beste kvalitet, men større filstørrelse" + "Lav kvalitet" + "Raskeste opplastingshastighet og minste filstørrelse" + "Standard kvalitet" + "Balansen mellom kvalitet og opplastingshastighet" "Talemelding" "Venter…" "Venter på denne meldingen" @@ -346,6 +373,10 @@ Er du sikker på at du vil fortsette?" "Dobbeltsjekk denne lenken" + "Velg standardkvaliteten på videoene du laster opp." + "Kvalitet på videoopplasting" + "Maksimal tillatt filstørrelse er: %1$s" + "Filstørrelsen er for stor til å kunne lastes opp" "Rommet er rapportert" "Rapportert og forlatt rommet" "Bekreftelse" @@ -354,6 +385,11 @@ Er du sikker på at du vil fortsette?" "Advarsel" "Endringene dine er ikke lagret. Er du sikker på at du vil gå tilbake?" "Lagre endringer?" + "Maksimal tillatt filstørrelse er: %1$s" + "Velg kvaliteten på videoen du vil laste opp." + "Velg kvalitet for videoopplasting" + "Søk etter emojier" + "Du er allerede logget inn på denne enheten som %1$s." "Hjemmeserveren din må oppgraderes for å støtte Matrix Authentication Service og kontooppretting." "Opprettelse av permalenken mislyktes" "%1$s kunne ikke laste inn kartet. Prøv igjen senere." @@ -404,6 +440,7 @@ Er du sikker på at du vil fortsette?" "Meldingen din ble ikke sendt fordi %1$s ikke har bekreftet alle enhetene" "En eller flere av enhetene dine er ikke verifisert. Du kan sende meldingen likevel, eller du kan avbryte og prøve igjen senere etter at du har verifisert alle enhetene dine." "Meldingen din ble ikke sendt fordi du ikke har verifisert en eller flere av enhetene dine" + "Rediger administratorer eller eiere" "Kunne ikke behandle medier for opplasting, vennligst prøv igjen." "Kunne ikke hente brukerdetaljer" "Melding i %1$s" @@ -423,6 +460,7 @@ Er du sikker på at du vil fortsette?" "Del denne lokasjonen" "Områder du har opprettet eller blitt med i." "%1$s • %2$s" + "%1$s område" "Områder" "Meldingen ble ikke sendt fordi %1$ss verifiserte identitet er tilbakestilt." "Meldingen ble ikke sendt fordi %1$s ikke har verifisert alle enheter." diff --git a/libraries/ui-strings/src/main/res/values-nl/translations.xml b/libraries/ui-strings/src/main/res/values-nl/translations.xml index 56d01c9727..7c682233a6 100644 --- a/libraries/ui-strings/src/main/res/values-nl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-nl/translations.xml @@ -361,4 +361,5 @@ Reden: %1$s." "Locatie" "Versie: %1$s (%2$s)" "en" + "Je hebt geen toegang tot dit bericht" diff --git a/libraries/ui-strings/src/main/res/values-pl/translations.xml b/libraries/ui-strings/src/main/res/values-pl/translations.xml index ebbad9ef86..71c60b5f6d 100644 --- a/libraries/ui-strings/src/main/res/values-pl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pl/translations.xml @@ -429,7 +429,7 @@ Czy na pewno chcesz kontynuować?" "pl" "Historia wiadomości nie jest dostępna na tym urządzeniu" "Musisz zweryfikować to urządzenie, aby uzyskać dostęp do historii wiadomości" - "Nie masz dostępu do tej wiadomości" + "Nie masz uprawnień do tej wiadomości" "Nie można odszyfrować wiadomości" "Wiadomość została zablokowana, ponieważ urządzenie nie zostało zweryfikowane lub nadawca musi zweryfikować Twoją tożsamość." diff --git a/libraries/ui-strings/src/main/res/values-pt/translations.xml b/libraries/ui-strings/src/main/res/values-pt/translations.xml index 0675f6e86d..5e55f3a9de 100644 --- a/libraries/ui-strings/src/main/res/values-pt/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pt/translations.xml @@ -80,6 +80,7 @@ "Recusar" "Recusar e bloquear" "Eliminar sondagem" + "Desselecionar tudo" "Desativar" "Descartar" "Dispensar" @@ -105,6 +106,7 @@ "Sair" "Sair da conversa" "Sair da sala" + "Sair do espaço" "Carregar mais" "Gerir conta" "Gerir dispositivos" @@ -114,7 +116,7 @@ "Agora não" "OK" "Abrir menu de contexto" - "Definições" + "Configurações" "Abrir com" "Afixar" "Resposta rápida" @@ -137,6 +139,7 @@ "Tentar decifragem novamente" "Guardar" "Pesquisar" + "Selecionar tudo" "Enviar" "Enviar mensagem editada" "Enviar mensagem" @@ -184,6 +187,7 @@ "A criar sala…" "Pedido cancelado" "Saíste da sala" + "Saíste do espaço" "Convite rejeitado" "Escuro" "Erro de decifragem" @@ -244,6 +248,7 @@ Razão: %1$s." "%1$s (%2$s)" "Sem resultados" "Sala sem nome" + "Espaço sem nome" "Não encriptado" "Desligado" "Licenças de código aberto" @@ -307,6 +312,7 @@ Razão: %1$s." "Servidor indisponível" "URL do servidor" "Configurações" + "Partilhar espaço" "Localização partilhada" "A terminar sessão" "Algo correu mal" @@ -453,6 +459,7 @@ Tens a certeza de que queres continuar?" "Partilhar este local" "Espaços que criaste ou nos quais entraste." "%1$s • %2$s" + "Espaço %1$s" "Espaços" "Mensagem não enviada porque a identidade verificada de %1$s foi reposta." "Mensagem não enviada porque %1$s não verificou todos os dispositivos." diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index ee2533cafa..0cf1acf673 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -44,7 +44,7 @@ "Îndepărtați reacția %1$s" "Avatarul camerei" "Trimiteți fișiere" - "Acțiune cu termen limită necesară" + "Acțiune limitată în timp necesară, aveți un minut pentru a verifica" "Afișați parola" "Începeți un apel" "Cameră terminată" @@ -82,6 +82,7 @@ "Refuzați" "Refuzați și blocați" "Ștergeți sondajul" + "Deselectați tot" "Dezactivați" "Renunţare" "Renunțați" @@ -96,6 +97,7 @@ "Ați uitat parola?" "Redirecționați" "Înapoi" + "Mergeți la setări" "Ignorați" "Invitați" "Invitați prieteni" @@ -107,10 +109,12 @@ "Părăsiți" "Părăsiți conversația" "Părăsiți camera" + "Părăsiți spațiul" "Încărcați mai mult" "Administrare cont" "Gestionare dispozitive" "Mesaj" + "Minimizați" "Următorul" "Nu" "Nu acum" @@ -139,6 +143,7 @@ "Reîncercați decriptarea" "Salvați" "Căutați" + "Selectați tot" "Trimiteți" "Trimiteți mesajul editat" "Trimiteți mesajul" @@ -167,6 +172,8 @@ "Upgrade disponibil" "Despre" "Politică de utilizare rezonabilă" + "Adăugați un cont" + "Adăugați un alt cont" "Adăugare descriere" "Setări avansate" "o imagine" @@ -175,6 +182,7 @@ "Ați fost deconectat din sesiune." "Aspect" "Audio" + "Beta" "Utilizatori blocați" "Baloane" "A început un apel" @@ -184,9 +192,11 @@ "Se creează camera…" "Cerere anulată" "Ați parăsit camera" + "S-a părăsit spațiul" "Invitația a fost refuzată" "Întunecat" "Eroare de decriptare" + "Descriere" "Opțiuni programator" "ID-ul dispozitivului" "Chat direct" @@ -221,6 +231,7 @@ Motiv:%1$s." "Instalați APK" "Nu am putut valida ID-ul Matrix al acestui utilizator. Este posibil ca invitația să nu fi fost trimisă." "Se părăsește conversația" + "Se părăsește spațiul" "Deschis" "Linie copiată în clipboard" "Linkul a fost copiat în clipboard" @@ -245,6 +256,7 @@ Motiv:%1$s." "%1$s (%2$s)" "Niciun rezultat" "Fără nume de cameră" + "Fără nume de spațiu" "Necriptat" "Deconectat" "Licențe open source" @@ -301,15 +313,19 @@ Motiv:%1$s." "Rezultatele căutării" "Securitate" "Văzut de" + "Selectați un cont" "Trimiteți către" "Se trimite…" "Trimiterea a eșuat" "Trimis" ". " "Serverul nu este compatibil" + "Serverul nu poate fi accesat" "Adresa URL a serverului" "Setări" + "Partajați spațiul" "Locație partajată" + "Spațiu comun" "Deconectare în curs" "Ceva nu a mers bine" "Am întâmpinat o problemă. Vă rugăm să încercați din nou." @@ -384,6 +400,8 @@ Sunteți sigur că doriți să continuați?" "Dimensiunea maximă permisă pentru fișiere este: %1$s" "Selectați calitatea videoclipului pe care doriți să îl încărcați." "Selectați calitatea de încărcare a videoclipurilor" + "Căutați emoticoane" + "Sunteți deja conectat pe acest dispozitiv ca %1$s." "Serverul dumneavoastră trebuie actualizat pentru a suporta serviciul de autentificare Matrix și crearea de conturi." "Crearea permalink-ului a eșuat" "%1$s nu a putut încărca harta. Vă rugăm să încercați din nou mai târziu." @@ -455,6 +473,7 @@ Sunteți sigur că doriți să continuați?" "Distribuiți această locație" "Spații pe care le-ați creat sau la care v-ați alăturat." "%1$s • %2$s" + "Spațiu %1$s" "Spații" "Mesajul nu a fost trimis deoarece identitatea verificată a lui %1$s s-a schimbat." "Mesajul nu a fost trimis deoarece %1$s nu a verificat toate dispozitivele." diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index d57db4ce60..fc856adf94 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -2,6 +2,7 @@ "Добавить реакцию:%1$s" "Аватар" + "Свернуть поле текста сообщения" "Удалить" "Введена %1$d цифра" @@ -11,6 +12,7 @@ "Изменить аватар" "Полный адрес %1$s" "Сведения о шифровании" + "Развернуть поле текста сообщения" "Скрыть пароль" "Присоединиться к звонку" "Перейти вниз" @@ -42,9 +44,10 @@ "Удалить реакцию %1$s" "Аватар комнаты" "Отправить файлы" - "Требуется срочное действие" + "Требуется действие, на которое есть ограничение по времени, у вас есть одна минута для проверки" "Показать пароль" "Начать звонок" + "Брошенная комната" "Аватар пользователя" "Меню пользователя" "Просмотреть аватар" @@ -79,6 +82,7 @@ "Отклонить" "Отклонить и заблокировать" "Удалить опрос" + "Отменить выбор" "Отключить" "Отменить" "Отклонить" @@ -104,10 +108,12 @@ "Покинуть" "Покинуть беседу" "Покинуть комнату" + "Покинуть пространство" "Загрузить еще" "Настройки учетной записи" "Управление устройствами" "Сообщение" + "Свернуть" "Далее" "Нет" "Не сейчас" @@ -136,6 +142,7 @@ "Повторите расшифровку" "Сохранить" "Поиск" + "Выбрать все" "Отправить" "Отправить изменённое сообщение" "Отправить сообщение" @@ -164,13 +171,17 @@ "Доступно обновление" "О приложении" "Политика допустимого использования" + "Добавить аккаунт" + "Добавить другой аккаунт" "Добавление подписи" "Дополнительные настройки" "изображение" "Аналитика" "Вы покинули комнату" + "Вы вышли из сеанса" "Внешний вид" "Аудио" + "Бета-версия" "Заблокированные пользователи" "Пузыри" "Звонок начат" @@ -180,9 +191,11 @@ "Создание комнаты…" "Запрос отменен" "Покинул комнату" + "Покинуть пространство" "Приглашение отклонено" "Тёмное" "Ошибка расшифровки" + "Описание" "Для разработчика" "Идентификатор устройства" "Личный чат" @@ -217,6 +230,7 @@ "Установить APK" "Идентификатор Matrix ID не найден, приглашение может быть не получено." "Покидание комнаты" + "Покинуть пространство" "Светлое" "Строка скопирована в буфер обмена" "Ссылка скопирована в буфер обмена" @@ -241,6 +255,7 @@ "%1$s (%2$s)" "Ничего не найдено" "Название комнаты отсутствует" + "Нет имени пространства" "Не зашифровано" "Не в сети" "Лицензии с открытым исходным кодом" @@ -285,6 +300,11 @@ "Комната" "Название комнаты" "например, название вашего проекта" + + "%1$d Комната" + "%1$d Комнат" + "%1$d Комнат" + "Изменения сохранены" "Сохранение" "Блокировка приложения" @@ -292,19 +312,27 @@ "Результаты поиска" "Безопасность" "Просмотрено" + "Выберите учетную запись" "Отправить" "Отправка…" "Сбой отправки" "Отправлено" ". " "Сервер не поддерживается" + "Сервер недоступен" "Адрес сервера" "Настройки" + "Поделиться пространством" "Поделился местоположением" "Выход…" "Что-то пошло не так" "Мы столкнулись с проблемой. Пожалуйста, попробуйте еще раз." "Подпространство" + + "%1$d Пространство" + "%1$d Пространств" + "%1$d Пространств" + "Чат запускается…" "Стикер" "Успешно" @@ -367,8 +395,11 @@ "Предупреждение" "Изменения не сохранены. Вы действительно хотите вернуться?" "Сохранить изменения?" + "Максимально допустимый размер файла: %1$s" "Выберите качество видео, которое вы хотите загрузить." "Выберите качество загружаемого видео" + "Поиск эмодзи" + "Вы уже вошли на данном устройстве как %1$s." "Ваш домашний сервер необходимо обновить, чтобы он поддерживал Matrix Authentication Service и создание учётных записей." "Не удалось создать постоянную ссылку" "Не удалось загрузить карту %1$s. Пожалуйста, повторите попытку позже." @@ -438,6 +469,10 @@ "Открыть в Google Maps" "Открыть в OpenStreetMap" "Поделиться этим местоположением" + "Пространства, которые вы создали или к которым присоединились." + "%1$s • %2$s" + "%1$s пространство" + "Пространства" "Сообщение не отправлено, потому что подтвержденная личность %1$s была сброшена." "Сообщение не отправлено, потому что %1$s не проверил одно или несколько устройств." "Сообщение не отправлено, поскольку вы не подтвердили одно или несколько своих устройств." @@ -446,7 +481,7 @@ "ru" "На этом устройстве недоступна история сообщений" "Вам необходимо проверить это устройство для доступа к истории сообщений" - "У вас нет доступа к этому сообщению" + "Вы не имеете доступа к этому сообщению" "Не удалось расшифровать сообщение" "Это сообщение было заблокировано по причине того, что вы не подтвердили свое устройство, либо отправителю необходимо подтвердить вашу личность." diff --git a/libraries/ui-strings/src/main/res/values-sv/translations.xml b/libraries/ui-strings/src/main/res/values-sv/translations.xml index 06b51baf80..e3cf26193e 100644 --- a/libraries/ui-strings/src/main/res/values-sv/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sv/translations.xml @@ -361,7 +361,7 @@ Anledning:%1$s." "Dubbelkolla den här länken" "Välj standardkvalitet för videor du laddar upp." "Videouppladdningskvalitet" - "Maximal tillåten filstorlek är: %1$s" + "Den maximala tillåtna filstorleken är: %1$s" "Filen är för stor för att laddas upp." "Rum anmält" "Anmälde och lämnade rummet" diff --git a/libraries/ui-strings/src/main/res/values-uk/translations.xml b/libraries/ui-strings/src/main/res/values-uk/translations.xml index 1afd19dc3a..a4c171a21b 100644 --- a/libraries/ui-strings/src/main/res/values-uk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-uk/translations.xml @@ -469,7 +469,7 @@ "uk" "Історичні повідомлення недоступні на цьому пристрої" "Щоб отримати доступ до історичних повідомлень, потрібно верифікувати цей пристрій" - "У вас немає доступу до цього повідомлення" + "Ви не маєте доступу до цього повідомлення" "Неможливо розшифрувати повідомлення" "Це повідомлення заблоковано оскільки ви не верифікували свій пристрій, або тому, що відправнику потрібно верифікувати вашу особистість." diff --git a/libraries/ui-strings/src/main/res/values-ur/translations.xml b/libraries/ui-strings/src/main/res/values-ur/translations.xml index 0262a99bf1..5abd2e0894 100644 --- a/libraries/ui-strings/src/main/res/values-ur/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ur/translations.xml @@ -326,4 +326,5 @@ "مقام" "نسخہ %1$s (%2$s)" "ur" + "آپ کو اس پیغام تک رسائی حاصل نہیں" diff --git a/libraries/ui-strings/src/main/res/values-uz/translations.xml b/libraries/ui-strings/src/main/res/values-uz/translations.xml index 4b6bb6a9f5..40da86f9d5 100644 --- a/libraries/ui-strings/src/main/res/values-uz/translations.xml +++ b/libraries/ui-strings/src/main/res/values-uz/translations.xml @@ -390,4 +390,5 @@ Sababi:%1$s." "Joylashuv" "Versiya:%1$s (%2$s )" "en" + "Sizni ushbu xabarga ruxsatingiz yoʻq" diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml index 93b486e173..346195863e 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml @@ -362,7 +362,7 @@ "仔細檢查此連結" "選取您上傳的視訊預設品質。" "視訊上傳品質" - "允許的最大檔案大小為:%1$s" + "最大允許的檔案大小為:%1$s" "檔案太大,無法上傳" "聊天室已回報" "回報並離開聊天室" @@ -404,9 +404,6 @@ "嘿,來 %1$s 和我聊天:%2$s" "%1$s Android" "憤怒搖晃以回報臭蟲" - "這也會將您從此空間中的所有聊天室移除。" - "這也會將您從此空間中的所有聊天室移除,包含您是唯一管理員的聊天室:" - "離開 %1$s?" "螢幕截圖" "%1$s:%2$s" "選項" @@ -458,7 +455,7 @@ "zh-tw" "歷史訊息在此裝置上無法讀取" "您必須驗證此裝置才能存取歷史訊息" - "您無權檢視此訊息" + "您無法存取此則訊息" "無法解密訊息" "此訊息被封鎖是因為您沒有驗證您的裝置,或是因為傳送者需要驗證您的身份而被封鎖。" diff --git a/libraries/ui-strings/src/main/res/values-zh/translations.xml b/libraries/ui-strings/src/main/res/values-zh/translations.xml index 951675ce6d..a4144eb8f0 100644 --- a/libraries/ui-strings/src/main/res/values-zh/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh/translations.xml @@ -78,6 +78,7 @@ "拒绝" "拒绝并屏蔽" "删除投票" + "取消全选" "禁用" "丢弃" "关闭" @@ -103,10 +104,12 @@ "离开" "离开聊天" "离开聊天室" + "离开空间" "载入更多" "管理账户" "管理设备" "发送消息给" + "最小化" "下一步" "否" "以后再说" @@ -135,6 +138,7 @@ "重试解密" "保存" "搜索" + "全选" "发送" "发送编辑后的消息" "发送消息" @@ -182,6 +186,7 @@ "正在创建聊天室…" "请求已取消" "离开聊天室" + "离开空间" "邀请已拒绝" "深色" "解密错误" @@ -220,6 +225,7 @@ "安装 APK" "找不到此 Matrix ID,因此可能无法收到邀请。" "正在离开聊天室" + "正在离开空间" "浅色" "链接已复制到剪贴板" "链接已复制到剪贴板" @@ -240,6 +246,7 @@ "%1$s (%2$s)" "没有结果" "无聊天室名" + "未命名空间" "未加密" "离线" "开源许可证" @@ -297,6 +304,7 @@ "已发送" "。 " "服务器不支持" + "无法访问服务器" "服务器 URL" "设置" "共享位置" @@ -452,7 +460,7 @@ "zh-Hans" "历史消息在此设备上不可用" "您需要验证此设备才能访问历史消息" - "您无权访问此消息" + "无权访问此消息" "无法解密消息" "此消息已被阻止,因为您未验证您的设备,或者发件人需要验证您的身份。" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 2e678e6c28..2484b0be64 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -80,6 +80,7 @@ "Decline" "Decline and block" "Delete Poll" + "Deselect all" "Disable" "Discard" "Dismiss" @@ -94,6 +95,7 @@ "Forgot password?" "Forward" "Go back" + "Go to settings" "Ignore" "Invite" "Invite people" @@ -110,6 +112,7 @@ "Manage account" "Manage devices" "Message" + "Minimise" "Next" "No" "Not now" @@ -138,6 +141,7 @@ "Retry decryption" "Save" "Search" + "Select all" "Send" "Send edited message" "Send message" @@ -176,6 +180,7 @@ "You were logged out of the session" "Appearance" "Audio" + "Beta" "Blocked users" "Bubbles" "Call started" @@ -224,6 +229,7 @@ Reason: %1$s." "Install APK" "This Matrix ID can\'t be found, so the invite might not be received." "Leaving room" + "Leaving space" "Light" "Line copied to clipboard" "Link copied to clipboard" @@ -312,6 +318,7 @@ Reason: %1$s." "Settings" "Share space" "Shared location" + "Shared space" "Signing out" "Something went wrong" "We encountered an issue. Please try again." @@ -414,9 +421,6 @@ Are you sure you want to continue?" "Hey, talk to me on %1$s: %2$s" "%1$s Android" "Rageshake to report bug" - "This will also remove you from all rooms in this space." - "This will also remove you from all rooms in this space, including those you’re the only administrator for:" - "Leave %1$s?" "Screenshot" "%1$s: %2$s" "Options" @@ -460,6 +464,7 @@ Are you sure you want to continue?" "Share this location" "Spaces you have created or joined." "%1$s • %2$s" + "%1$s space" "Spaces" "Message not sent because %1$s’s verified identity was reset." "Message not sent because %1$s has not verified all devices." diff --git a/libraries/voiceplayer/impl/src/main/kotlin/io/element/android/libraries/voiceplayer/impl/VoiceMessageMediaRepo.kt b/libraries/voiceplayer/impl/src/main/kotlin/io/element/android/libraries/voiceplayer/impl/VoiceMessageMediaRepo.kt index ccee1c4c18..a1f4de2087 100644 --- a/libraries/voiceplayer/impl/src/main/kotlin/io/element/android/libraries/voiceplayer/impl/VoiceMessageMediaRepo.kt +++ b/libraries/voiceplayer/impl/src/main/kotlin/io/element/android/libraries/voiceplayer/impl/VoiceMessageMediaRepo.kt @@ -9,8 +9,8 @@ package io.element.android.libraries.voiceplayer.impl import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.ContributesBinding -import dev.zacsweers.metro.Inject import io.element.android.libraries.core.extensions.mapCatchingExceptions import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.di.RoomScope @@ -56,7 +56,7 @@ interface VoiceMessageMediaRepo { suspend fun getMediaFile(): Result } -@Inject +@AssistedInject class DefaultVoiceMessageMediaRepo( @CacheDirectory private val cacheDir: File, mxcTools: MxcTools, diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 96a308f93f..52c09434d4 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -38,13 +38,13 @@ private const val versionYear = 25 * Month of the version on 2 digits. Value must be in [1,12]. * Do not update this value. it is updated by the release script. */ -private const val versionMonth = 9 +private const val versionMonth = 10 /** * 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 = 0 object Versions { /** diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt index 2b6c531eda..d46c54e1c3 100644 --- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt +++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt @@ -107,6 +107,7 @@ fun DependencyHandlerScope.allLibrariesImpl() { implementation(project(":libraries:mediaupload:impl")) implementation(project(":libraries:usersearch:impl")) implementation(project(":libraries:textcomposer:impl")) + implementation(project(":libraries:accountselect:impl")) implementation(project(":libraries:roomselect:impl")) implementation(project(":libraries:cryptography:impl")) implementation(project(":libraries:voiceplayer:impl")) diff --git a/screenshots/de/features.announcement.impl.spaces_SpaceAnnouncementView_Day_0_de.png b/screenshots/de/features.announcement.impl.spaces_SpaceAnnouncementView_Day_0_de.png new file mode 100644 index 0000000000..6c1f2b970f --- /dev/null +++ b/screenshots/de/features.announcement.impl.spaces_SpaceAnnouncementView_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:064b8f0aae8eefbac4b70f7836bbd41d27954f8c7c580f7f306fb8fad2ee1022 +size 69465 diff --git a/screenshots/de/features.home.impl.components_DefaultRoomListTopBarMultiAccount_Day_0_de.png b/screenshots/de/features.home.impl.components_DefaultRoomListTopBarMultiAccount_Day_0_de.png new file mode 100644 index 0000000000..6cfe101073 --- /dev/null +++ b/screenshots/de/features.home.impl.components_DefaultRoomListTopBarMultiAccount_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:baac8c8251f75553c31d68ab3c56435303fcf918ebdcbe5986f96222112f4b2c +size 26452 diff --git a/screenshots/de/features.home.impl.components_NewNotificationSoundBanner_Day_0_de.png b/screenshots/de/features.home.impl.components_NewNotificationSoundBanner_Day_0_de.png new file mode 100644 index 0000000000..cbaec95e4e --- /dev/null +++ b/screenshots/de/features.home.impl.components_NewNotificationSoundBanner_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91a7fc2676cb7271eb85ffe1e8bf42892a0f40fa4b8310f3385afd790f28c17f +size 25724 diff --git a/screenshots/de/features.home.impl.components_RoomListContentView_Day_5_de.png b/screenshots/de/features.home.impl.components_RoomListContentView_Day_5_de.png new file mode 100644 index 0000000000..d1173d8bb1 --- /dev/null +++ b/screenshots/de/features.home.impl.components_RoomListContentView_Day_5_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ecfd2bb5f3fa6d1828dd63ffb6b97169b138d1899f999b3a40f8f45c6b74297 +size 64057 diff --git a/screenshots/de/features.home.impl.components_RoomSummaryRow_Day_32_de.png b/screenshots/de/features.home.impl.components_RoomSummaryRow_Day_32_de.png index 707e02b860..323bc366ef 100644 --- a/screenshots/de/features.home.impl.components_RoomSummaryRow_Day_32_de.png +++ b/screenshots/de/features.home.impl.components_RoomSummaryRow_Day_32_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d51f365f53e6d513995ff06dd428b2093bd3410e50ccd0f140f9b187ed72932b -size 13055 +oid sha256:2b02cde7ac34031dac3ab62cbc88bac96475c287aa7edbe7c057edfbfbfbc91f +size 23478 diff --git a/screenshots/de/features.home.impl.components_RoomSummaryRow_Day_33_de.png b/screenshots/de/features.home.impl.components_RoomSummaryRow_Day_33_de.png index 86e74552cf..707e02b860 100644 --- a/screenshots/de/features.home.impl.components_RoomSummaryRow_Day_33_de.png +++ b/screenshots/de/features.home.impl.components_RoomSummaryRow_Day_33_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be3903803196a109c756058a33f278a268013e953073427afe1469992c921e8f -size 18424 +oid sha256:d51f365f53e6d513995ff06dd428b2093bd3410e50ccd0f140f9b187ed72932b +size 13055 diff --git a/screenshots/de/features.home.impl.components_RoomSummaryRow_Day_34_de.png b/screenshots/de/features.home.impl.components_RoomSummaryRow_Day_34_de.png index 349100c47f..86e74552cf 100644 --- a/screenshots/de/features.home.impl.components_RoomSummaryRow_Day_34_de.png +++ b/screenshots/de/features.home.impl.components_RoomSummaryRow_Day_34_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7447fa97dde37796951804ff2e19a9e2fcd83f9ef0acd62d8591fca467a6c59f -size 15918 +oid sha256:be3903803196a109c756058a33f278a268013e953073427afe1469992c921e8f +size 18424 diff --git a/screenshots/de/features.home.impl.components_RoomSummaryRow_Day_35_de.png b/screenshots/de/features.home.impl.components_RoomSummaryRow_Day_35_de.png new file mode 100644 index 0000000000..349100c47f --- /dev/null +++ b/screenshots/de/features.home.impl.components_RoomSummaryRow_Day_35_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7447fa97dde37796951804ff2e19a9e2fcd83f9ef0acd62d8591fca467a6c59f +size 15918 diff --git a/screenshots/de/features.home.impl.spaces_HomeSpacesView_Day_0_de.png b/screenshots/de/features.home.impl.spaces_HomeSpacesView_Day_0_de.png index 0f5f4cf56a..a6e961cacb 100644 --- a/screenshots/de/features.home.impl.spaces_HomeSpacesView_Day_0_de.png +++ b/screenshots/de/features.home.impl.spaces_HomeSpacesView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b13217ac65a45328a20c1bbb9ee6acb8f9a64ee85b8101118ae8d5a958a0104f -size 109579 +oid sha256:2f5dc2dfed0bc7951d5ddc45215fa7798157d5d807574ca4724549d706c56bea +size 90925 diff --git a/screenshots/de/features.home.impl.spaces_HomeSpacesView_Day_1_de.png b/screenshots/de/features.home.impl.spaces_HomeSpacesView_Day_1_de.png index 07a22f07bc..5521655e1a 100644 --- a/screenshots/de/features.home.impl.spaces_HomeSpacesView_Day_1_de.png +++ b/screenshots/de/features.home.impl.spaces_HomeSpacesView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1533f3ace12c3822860044ba4e4ee4a5eb0e62e82dc83b16bc469ff47b6b5d1 -size 42891 +oid sha256:5a5878f4483056c91981bdaed225a5dfabb78ac3f55243c2f7323f259c00542e +size 42026 diff --git a/screenshots/de/features.home.impl_HomeView_Day_4_de.png b/screenshots/de/features.home.impl_HomeView_Day_4_de.png index eaa124d391..1c1734ceee 100644 --- a/screenshots/de/features.home.impl_HomeView_Day_4_de.png +++ b/screenshots/de/features.home.impl_HomeView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c21f392c15e20ade2ae53c43c97abd7f5e716c29deb9db9be387de3f25e40f59 -size 60291 +oid sha256:2c6870cbb8fcca652d06ff492a900ef01f0272129ac6777e6f207c5c97dd5bfd +size 56530 diff --git a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_10_de.png b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_10_de.png index 150c43d045..f0f67e6a31 100644 --- a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_10_de.png +++ b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d1f18048ebb898d43d41c4e465020f5339cae3d973e62b17f1f6df5f5aea2e8 -size 46191 +oid sha256:dcf5e51d91e309bfe97f696d45a0a3ef19c9139de30f3bbd0c1cd5cbb2aee22e +size 46807 diff --git a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_11_de.png b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_11_de.png index 5a8020b57f..4ae703b23e 100644 --- a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_11_de.png +++ b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2ae8aadc032491f8be5abbc838a17d6277e723c5a8c3863155b9f1aadb7f7a4 -size 43215 +oid sha256:9399942211a13c9d871692dfd1b421fcd923ddde9f5922979ebfff07c0824060 +size 41067 diff --git a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_12_de.png b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_12_de.png index d02438d136..5dcd4a2ba3 100644 --- a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_12_de.png +++ b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c769a2008a85b425ca02f43fb3b7440ff0decb203379916152b306e7742d326 -size 44438 +oid sha256:296d74f3a3198f98599fd2c428268f67b4b70e09fdfbeead9f7c7436e9cf8758 +size 42259 diff --git a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_13_de.png b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_13_de.png index e9a944b26a..f38dfd26a6 100644 --- a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_13_de.png +++ b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8c184486658ccce3de7c5ce40bf761bd783e4e9bcb8da3c62d35ab5760509a89 -size 34706 +oid sha256:81ae149fcf6f1a6a52bb90618b2bd6b79192221d2decadd3d78f34ac227a0e86 +size 34158 diff --git a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_14_de.png b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_14_de.png index ea86986f2e..5637757b98 100644 --- a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_14_de.png +++ b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_14_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e5497c9cceb557334a19e28e9c57a036c06162f13e35c7427245d91bd6e11061 -size 31479 +oid sha256:26c22c76a3a13cdda38f9069adef8d6f17e2b5bf6bfa952812bde3575f62281f +size 31974 diff --git a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_15_de.png b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_15_de.png index 351c136cba..06bb84c272 100644 --- a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_15_de.png +++ b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_15_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:910721deff35d8d8a93494327bf5635ae298877e382f19cdacdb4116ffef792e -size 38164 +oid sha256:fe3ea3e63387d40e0314b702a0e08bba8bee8b63dac6758fec713a929715b92c +size 35827 diff --git a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_16_de.png b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_16_de.png index a3761dfe34..5cd8ca5e77 100644 --- a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_16_de.png +++ b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_16_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:14e852ebdeb6e6f160c226123fe7e2d8b1b7eaa37417782f088c6f7522863e13 -size 48628 +oid sha256:6367c2cb513ab5e26d3e16a1a7554251cd5051b55328cae99019eac84e6ef2bd +size 49735 diff --git a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_1_de.png b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_1_de.png index fd88b8e091..4cadf347c5 100644 --- a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_1_de.png +++ b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ce1383194049c3f636ace5110df83193c35cd7283046bac7338418f7835a5aa -size 38251 +oid sha256:ff18fe6020711f81385f8e9e54fb685d49f95dbb720fdab37de2acc35896647b +size 41101 diff --git a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_2_de.png b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_2_de.png index 99e8fabab0..013b1aa259 100644 --- a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_2_de.png +++ b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba401bb1c024b4d1ed056d589f80e025606ac34654e3e376cc9eadd8017863a7 -size 40077 +oid sha256:f96ada2da82b7265fc655e4826069b395e07dedb28ad86761f956e398ced79cf +size 39667 diff --git a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_3_de.png b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_3_de.png index dace55d0da..d822cfb9f7 100644 --- a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_3_de.png +++ b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:47aa0258eb4b28e4c34c9425b3cfab8b5746efc1f3a4a6680cfa24daab05d9c9 -size 27787 +oid sha256:4a6338d8c9baed45e73eb8eab8f9b097a708f1b2c0864de80911d8ddc3f9c361 +size 29854 diff --git a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_4_de.png b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_4_de.png index 7bb106c8ed..6744cd39a9 100644 --- a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_4_de.png +++ b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c7945c197b13ebcc463e2655be0642acfdc62e887d7bdc3c12ffe329d586b61 -size 45690 +oid sha256:f0fb1a5fc06cd64bcdb5b75e56fdaeccad017bf17b11c8cb8f9714e9c7eaab2c +size 46214 diff --git a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_5_de.png b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_5_de.png index 8b08ed808a..b3a4eeb0d5 100644 --- a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_5_de.png +++ b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8600c62cc6145823871ecaa23f43968669ef25c3b25a3504478bb3919ede19ee -size 31953 +oid sha256:428dfda0652172cc9e9a2392db535fc51de5280b060d969863d0299142c1f35d +size 34643 diff --git a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_6_de.png b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_6_de.png index db8fec3c75..02b18e602d 100644 --- a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_6_de.png +++ b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7cdbd64bf415dd7c35f59387f6fc3660c692af704c9dfd8d3cd96c814b3079f4 -size 31656 +oid sha256:1292c78624d48944ba94da85c8e56f2636d059458bbc330298dcca89131c143f +size 34720 diff --git a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_7_de.png b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_7_de.png index 68c0f56eac..5b96ed77a8 100644 --- a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_7_de.png +++ b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30b3eafeb3167a792b9543d7cb1ea033b8add88435b7c7721dd840e8dbc9537d -size 40476 +oid sha256:3d40fe4aa6b06f8b52ec5b4f94549c35613872b3c84843e814e400139c1ba1cd +size 41872 diff --git a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_8_de.png b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_8_de.png index 1e052632fd..799892693d 100644 --- a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_8_de.png +++ b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c964465b659870b6ce5b32eab2ba8c19d0b078b06660c3f14faf6e54c029763d -size 34001 +oid sha256:287dc0a75daeafebb50f79ec327a02eb019d3016b2e52e28613edf9c6e252315 +size 35560 diff --git a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_9_de.png b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_9_de.png index 22ad909349..b7606cbfb3 100644 --- a/screenshots/de/features.joinroom.impl_JoinRoomView_Day_9_de.png +++ b/screenshots/de/features.joinroom.impl_JoinRoomView_Day_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:236cb5e5e782577f8a4376717daac8a93f7c111b8093f849de654c5dc9f1bf8a -size 35731 +oid sha256:d097d473593e6cc6bd0cc7bc13ea40faaad312dde2450170c0bfaa7a96e7d2bd +size 43139 diff --git a/screenshots/de/features.login.impl.login_LoginModeView_Day_5_de.png b/screenshots/de/features.login.impl.login_LoginModeView_Day_5_de.png new file mode 100644 index 0000000000..d8ed4858b8 --- /dev/null +++ b/screenshots/de/features.login.impl.login_LoginModeView_Day_5_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c0e1ea6e6a244f7b1329aaa4ef699d059e9fd137c58a8e78c0e5fc43f2d45832 +size 18149 diff --git a/screenshots/de/features.login.impl.screens.onboarding_OnBoardingView_Day_5_de.png b/screenshots/de/features.login.impl.screens.onboarding_OnBoardingView_Day_5_de.png index 50eb5ade2c..ea13fb0d29 100644 --- a/screenshots/de/features.login.impl.screens.onboarding_OnBoardingView_Day_5_de.png +++ b/screenshots/de/features.login.impl.screens.onboarding_OnBoardingView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dcceab75e6d5fa22a9f306f1ac7f164b2d69432e36af29bcfb89684725e31dc5 -size 317545 +oid sha256:a5464e2976a5030545b28279cea8129c3bf4722f20adc58b2567b1318d68d2f6 +size 317591 diff --git a/screenshots/de/features.login.impl.screens.onboarding_OnBoardingView_Day_7_de.png b/screenshots/de/features.login.impl.screens.onboarding_OnBoardingView_Day_7_de.png new file mode 100644 index 0000000000..247acb90dc --- /dev/null +++ b/screenshots/de/features.login.impl.screens.onboarding_OnBoardingView_Day_7_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5dd50e66c81b2cdd21c7d3ebe9fa1ae1c56c43c271a9ffa57a6868b9e19d230e +size 22756 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_11_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_11_de.png index b7d0a5b6c3..4632f2d972 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_11_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3bd314ce626efbc0bb31a86012972434a1a7f4e828cfc83c3622303550a0293d -size 55044 +oid sha256:37b929271600ddab0836c758faeabf3a810fab4fc93b6fee1821cfd627aaf145 +size 55370 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_12_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_12_de.png index 2c63482830..bb7b2152e4 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_12_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:507e3abe31d7360c16962484136c69a41e9b8ff36354d442da59ac90865e7a47 -size 55542 +oid sha256:5c0cf0fe1a0dbeb9808fac60cae7a9303254f8004bd8809242a372a468a0a749 +size 55912 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_2_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_2_de.png index b440fc9ec1..d53e510d4f 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_2_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f62483754a8ecf20740d5bc37a00b0aa3b810c005998f22bf8ce5ee7e3cf6cb -size 46877 +oid sha256:0231fe2d6f4d97ff35dea16c2cefbd19bde7e1a9d7814cb1bac5d43cb647ded6 +size 47281 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_3_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_3_de.png index ba0b44793e..698df60f9a 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_3_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e63e08e5d9a32dde4f18f4fbcb74744c0e41d012f3d8e68559c9a72555ad661d -size 51201 +oid sha256:1b29b1f2e1f8b339e608af09b9f49a6cb664c9a37130c312c75ca9ab29041665 +size 51544 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_4_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_4_de.png index 93921db5b2..4d396b0f86 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_4_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3314f45bc6d2301753aceadd481a35f82399f2a36855e17fe68e33b07132d91 -size 49587 +oid sha256:0f11362ebac1674b839f8adb9d749d9486b1bbd59c71a2ddae6c333cb0cc0a84 +size 49963 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_5_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_5_de.png index 0d54979924..7759741a6f 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_5_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2ce1310b330f6191cd3490881b0b3272e296414b492a957a7a2c0fa9f356fdc -size 45284 +oid sha256:18862f4d1303abde8f155f38d9057db7b9d990b288e7587dc58f94636f5927e0 +size 45504 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_6_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_6_de.png index 8cee3597e3..4b978064ec 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_6_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66cea759a333ac35a55a6ff09ef35f5754ac615ec7430b9a56fe36c9f4e14262 -size 49854 +oid sha256:1882aaff8f678953c313b70ecc9d52d7b0e46ef29162b3cc8a24ced0704a7069 +size 50221 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_7_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_7_de.png index 26bee2425d..667c230b2a 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_7_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c13ef69c250f617da35ce7e2872afdf427e5b3a5427b3a25d3221950eca33119 -size 46166 +oid sha256:17c547e8a275eefac0de72ca58f6ec3a81e80c8878900a63723c5ccc738270a6 +size 46267 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_8_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_8_de.png index 03ae9bffac..68a9eeb68f 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_8_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a13eea9ce58656559442de31c8323fabb7e9aee23b247e6a6b9d3b6e5680851e -size 49020 +oid sha256:2299b225ffd05ed2e69e5f5c46c0623a0e54014cd1419ef1ce371b87b803e347 +size 49293 diff --git a/screenshots/de/features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_de.png b/screenshots/de/features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_de.png index acd345bb23..49936a3ea5 100644 --- a/screenshots/de/features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_de.png +++ b/screenshots/de/features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0fc2bc8a43407a1ab3d01cc3e0f584e7964b1c4e460e30c3c5042c49055d8e8b -size 21531 +oid sha256:8ef8f25ec439986622c3ad9bf9a30ece6d3f7e17f264b7260203ee73da4051e7 +size 22243 diff --git a/screenshots/de/features.messages.impl.timeline.components_ThreadSummaryView_Day_0_de.png b/screenshots/de/features.messages.impl.timeline.components_ThreadSummaryView_Day_0_de.png new file mode 100644 index 0000000000..9f240daeae --- /dev/null +++ b/screenshots/de/features.messages.impl.timeline.components_ThreadSummaryView_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18a7f4dbf3a56c49e5b1edbd0f419620dbfcd4485bfe25db5347968b8844973b +size 9933 diff --git a/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_de.png b/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_de.png index b77d099522..0de6e5f19e 100644 --- a/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_de.png +++ b/screenshots/de/features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d49ca428a5b9780d2d6aea785b2187853675c607fd50c09422179c911ee47c34 -size 70557 +oid sha256:e8b14f1fba1b0faa17b331d442e780456f7d0968e50d46d4c4352ff56d2a7bda +size 68148 diff --git a/screenshots/de/features.messages.impl.topbars_MessagesViewTopBar_Day_0_de.png b/screenshots/de/features.messages.impl.topbars_MessagesViewTopBar_Day_0_de.png new file mode 100644 index 0000000000..d6e752b972 --- /dev/null +++ b/screenshots/de/features.messages.impl.topbars_MessagesViewTopBar_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8348ac5ab0a32d80f7566bd734ace5ed549d55e1d003571e0e7fe3a68986f7dd +size 42668 diff --git a/screenshots/de/features.messages.impl.topbars_ThreadTopBar_Day_0_de.png b/screenshots/de/features.messages.impl.topbars_ThreadTopBar_Day_0_de.png new file mode 100644 index 0000000000..77c579fc16 --- /dev/null +++ b/screenshots/de/features.messages.impl.topbars_ThreadTopBar_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f05ab027f2840e839337a59656cbf3b0f526e45ef8abcfb4677585a559e14fd7 +size 33920 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_10_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_10_de.png index e4e0f50f76..92fb37b785 100644 --- a/screenshots/de/features.messages.impl_MessagesView_Day_10_de.png +++ b/screenshots/de/features.messages.impl_MessagesView_Day_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e1c039c775e3b3ace5c4f6b21b91ea027383f007a9c8c4743c3485c05aca2c8 -size 58976 +oid sha256:5969d4a287e7a6f589639569da53d97c8adf7c190d05776f5342084d4344c886 +size 53839 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_11_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_11_de.png deleted file mode 100644 index d0b40bab37..0000000000 --- a/screenshots/de/features.messages.impl_MessagesView_Day_11_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:12e6c4fa5f0fffd03a9ef247a41d64e3ac579b6a5b803fcf0fb89fcc71bee9c7 -size 62309 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_12_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_12_de.png deleted file mode 100644 index f7cba2d2ec..0000000000 --- a/screenshots/de/features.messages.impl_MessagesView_Day_12_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d8d3649c61ee7d08f83426fc766819663302051149ada8e4cbf612be9557991c -size 61237 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_13_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_13_de.png deleted file mode 100644 index f64dcd2e47..0000000000 --- a/screenshots/de/features.messages.impl_MessagesView_Day_13_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3d699f41550104cd5c8cb5744fffd2f966b60f5cf8a8472e17efc15c2df25346 -size 61165 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_14_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_14_de.png deleted file mode 100644 index ea6e3ef99b..0000000000 --- a/screenshots/de/features.messages.impl_MessagesView_Day_14_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2439aecde9e9ad584cc7e06c6483d66af120d5abbb4915381be6b091f51168ca -size 66206 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_15_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_15_de.png deleted file mode 100644 index aa674b74cb..0000000000 --- a/screenshots/de/features.messages.impl_MessagesView_Day_15_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:687385fa7dfbd65229cea2c305c6ccaa6377b13acade6a7dd523b3dc125f533e -size 51850 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_5_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_5_de.png index 440f2e32d4..7c1807d837 100644 --- a/screenshots/de/features.messages.impl_MessagesView_Day_5_de.png +++ b/screenshots/de/features.messages.impl_MessagesView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:62831c3c86227d5850d962dcfc2b0b897d99bbb3b4a2497a9efa6131c476b159 -size 60478 +oid sha256:9803a08e9c42e821941c85f596e909755723680f75534104f297a6f2bcb37a7d +size 55309 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_6_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_6_de.png index 7c1807d837..9a48de10cb 100644 --- a/screenshots/de/features.messages.impl_MessagesView_Day_6_de.png +++ b/screenshots/de/features.messages.impl_MessagesView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9803a08e9c42e821941c85f596e909755723680f75534104f297a6f2bcb37a7d -size 55309 +oid sha256:9ef54103492179e8003b304ec1554873ac031d18b1bb00aeb65767b403b58915 +size 61819 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_7_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_7_de.png index 9a48de10cb..93a2e19fca 100644 --- a/screenshots/de/features.messages.impl_MessagesView_Day_7_de.png +++ b/screenshots/de/features.messages.impl_MessagesView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ef54103492179e8003b304ec1554873ac031d18b1bb00aeb65767b403b58915 -size 61819 +oid sha256:ceff1004db2bbe6cbdf2c6d62e4521b89a9c63823b6cd1c709037e3bae306302 +size 49568 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_8_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_8_de.png index 0bd6113fae..d0b40bab37 100644 --- a/screenshots/de/features.messages.impl_MessagesView_Day_8_de.png +++ b/screenshots/de/features.messages.impl_MessagesView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b206dd36fff869ebbff8a63c783cab6f24998458cd6bfe8af397a28209cc7894 -size 61425 +oid sha256:12e6c4fa5f0fffd03a9ef247a41d64e3ac579b6a5b803fcf0fb89fcc71bee9c7 +size 62309 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_9_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_9_de.png index 93a2e19fca..ea6e3ef99b 100644 --- a/screenshots/de/features.messages.impl_MessagesView_Day_9_de.png +++ b/screenshots/de/features.messages.impl_MessagesView_Day_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ceff1004db2bbe6cbdf2c6d62e4521b89a9c63823b6cd1c709037e3bae306302 -size 49568 +oid sha256:2439aecde9e9ad584cc7e06c6483d66af120d5abbb4915381be6b091f51168ca +size 66206 diff --git a/screenshots/de/features.preferences.impl.labs_LabsView_Day_0_de.png b/screenshots/de/features.preferences.impl.labs_LabsView_Day_0_de.png new file mode 100644 index 0000000000..972332f722 --- /dev/null +++ b/screenshots/de/features.preferences.impl.labs_LabsView_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2170c55c31cc2065787a634fde443fea1f4f5acd1d7a7cce14f8255447b2e8bb +size 48317 diff --git a/screenshots/de/features.preferences.impl.labs_LabsView_Day_1_de.png b/screenshots/de/features.preferences.impl.labs_LabsView_Day_1_de.png new file mode 100644 index 0000000000..b21729fa34 --- /dev/null +++ b/screenshots/de/features.preferences.impl.labs_LabsView_Day_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2a065f3e8983d7fd2cf363984ca07f69bd2e0841d5302e7b2ac2c60a259332e2 +size 39484 diff --git a/screenshots/de/features.preferences.impl.root_MultiAccountSection_Day_0_de.png b/screenshots/de/features.preferences.impl.root_MultiAccountSection_Day_0_de.png new file mode 100644 index 0000000000..2ab16568a9 --- /dev/null +++ b/screenshots/de/features.preferences.impl.root_MultiAccountSection_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2505c99e7ce75631696f09774c312363d414856bd2cdeece40a5bbfb04f38184 +size 59248 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_0_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_0_de.png index 008fe3e36b..8b4e8902b2 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_0_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d88d7dbbe66c64573df0e237e613be676533628bea149fe69d5631a098f4d18 -size 40286 +oid sha256:b63b8a1a2e1f6f3eb66d567103184177bd5fed9dafceb1e9d0900465a648d009 +size 42080 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_1_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_1_de.png index 096c9d3ada..8bd32ee5ad 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_1_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7010114695842036c447c44a91d194fcdb6423d8e6f50a0fe82237e8c896b877 -size 40136 +oid sha256:f62deb4536b2b626505244590ce5d6531931f1cfbb87d5d97c2faa0db79d691f +size 41904 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_0_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_0_de.png index 94d0c20377..e9d0333419 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_0_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3236e2feff08195a5c02daca103aa6619132ddaa9f0b7ae0daa7fd048319f91c -size 41311 +oid sha256:d97d1234293a227f015f4eccc4adbe95fd97f2b28b9a0205ec558bd6ae287296 +size 43272 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_1_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_1_de.png index 925834be9c..723e56cafb 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_1_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58eabf09ccf68f7d2157a4ce629a2ec5cd3f07ee596712adf5d5eaba0b7d028e -size 41361 +oid sha256:95662f13be6b716b5d5c5682f4a3cc5ad822a8899c677fdafd818c24b0e0d7d8 +size 43313 diff --git a/screenshots/de/features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_de.png b/screenshots/de/features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_de.png index 1e052632fd..16e8224c33 100644 --- a/screenshots/de/features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_de.png +++ b/screenshots/de/features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c964465b659870b6ce5b32eab2ba8c19d0b078b06660c3f14faf6e54c029763d -size 34001 +oid sha256:544cbdde4d635c702e64c4279923527a10a5fc5bb199cc074e44d36208dcf1fb +size 33946 diff --git a/screenshots/de/features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_de.png b/screenshots/de/features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_de.png index 17ecc2d044..05d32f6e21 100644 --- a/screenshots/de/features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_de.png +++ b/screenshots/de/features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7010c3f8fcd3863d07a97ea8e07be0ce65f9a9d232b46c99888bade57462e9a6 -size 25589 +oid sha256:46650e2769751b04dae2cc0edf05d628678dbfa73439d207f3cca75379dab8bd +size 27744 diff --git a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_4_de.png b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_4_de.png index f11c344ae6..eb8f0f1e95 100644 --- a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_4_de.png +++ b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6dd52f4b17d3ff94b4d61db544fddfb8b449da49673ecb45a02fa297966b52f6 -size 31604 +oid sha256:dfe0aa4dc149bda0330dd662708f243d3a99d05a2d419e02dd364d0898c29365 +size 34129 diff --git a/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_0_de.png b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_0_de.png new file mode 100644 index 0000000000..fd00f875fe --- /dev/null +++ b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe4e5c4f1defe3b99adcf563eb389c943f93d5417f2344eff2312f2b809991c7 +size 15572 diff --git a/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_1_de.png b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_1_de.png new file mode 100644 index 0000000000..a56d30a522 --- /dev/null +++ b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58812339f1ac01175b64421d0af685330c4bd95186eff5213194e573deeaa443 +size 18292 diff --git a/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_2_de.png b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_2_de.png new file mode 100644 index 0000000000..8a3fdd24f4 --- /dev/null +++ b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_2_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:565857c30ef349022e9cddfcd892a481858ba83bc3dacf884ae37be964fe327b +size 46496 diff --git a/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_3_de.png b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_3_de.png new file mode 100644 index 0000000000..94f10f54ac --- /dev/null +++ b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_3_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5424035b81a1cf082a6a56450bfc444810ffb7d0af87d0803e99151c9cdb3cb4 +size 46000 diff --git a/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_4_de.png b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_4_de.png new file mode 100644 index 0000000000..7f36068a32 --- /dev/null +++ b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_4_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a39c417a6d8f4d8303d9b2d5c229ffbc55921bb706161816c5f774b1aa5739f +size 36889 diff --git a/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_5_de.png b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_5_de.png new file mode 100644 index 0000000000..a234c6588b --- /dev/null +++ b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_5_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fba6f766f3c7cc257d4e199389f03290e63dd2caa040329648969caabfbb5869 +size 43746 diff --git a/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_6_de.png b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_6_de.png new file mode 100644 index 0000000000..f942304d6f --- /dev/null +++ b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_6_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:def46d1907ba93766cf6b4d465840820221800d2d4e34a14b757697d73b17623 +size 44009 diff --git a/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_7_de.png b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_7_de.png new file mode 100644 index 0000000000..bc853d286e --- /dev/null +++ b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_7_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec8a73647a8cadd203006313fb3baa79e27f9e8faaef991d7e37c0faccc67281 +size 42259 diff --git a/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_8_de.png b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_8_de.png new file mode 100644 index 0000000000..01c57b8085 --- /dev/null +++ b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_8_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0e86c1032701041744144e88185238129ed824b2e4cb0c4992d6cceae2322442 +size 20060 diff --git a/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_9_de.png b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_9_de.png new file mode 100644 index 0000000000..99d948fd8b --- /dev/null +++ b/screenshots/de/features.space.impl.leave_LeaveSpaceView_Day_9_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79eceaf8ae38ae53f4e2736f64628b36d5f4201d42206cfe48b547334800165f +size 30296 diff --git a/screenshots/de/features.space.impl.root_SpaceView_Day_0_de.png b/screenshots/de/features.space.impl.root_SpaceView_Day_0_de.png new file mode 100644 index 0000000000..ee8e0d3535 --- /dev/null +++ b/screenshots/de/features.space.impl.root_SpaceView_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:776ffc78757ec0ed2863407e91d6c2071dc40e945ef9448bc405a35abfd1c925 +size 19403 diff --git a/screenshots/de/features.space.impl.root_SpaceView_Day_1_de.png b/screenshots/de/features.space.impl.root_SpaceView_Day_1_de.png new file mode 100644 index 0000000000..8ca4805dc7 --- /dev/null +++ b/screenshots/de/features.space.impl.root_SpaceView_Day_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b63f17d6bf0d3b18c0e405a4c14a4a68a8351c192fa788fc5135d4fbe8269d85 +size 20383 diff --git a/screenshots/de/features.space.impl.root_SpaceView_Day_2_de.png b/screenshots/de/features.space.impl.root_SpaceView_Day_2_de.png new file mode 100644 index 0000000000..411c33cc5c --- /dev/null +++ b/screenshots/de/features.space.impl.root_SpaceView_Day_2_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba5eb40aa5ba4e2ebe6e68ad42d1b27080c3680a86a613d538344c2b8dadc6f6 +size 20710 diff --git a/screenshots/de/features.space.impl.root_SpaceView_Day_3_de.png b/screenshots/de/features.space.impl.root_SpaceView_Day_3_de.png new file mode 100644 index 0000000000..0cca59b427 --- /dev/null +++ b/screenshots/de/features.space.impl.root_SpaceView_Day_3_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:95ef629af8452fe2576011db8c753b23d9b45b51d2a33bfa8a17c861474a99d2 +size 20869 diff --git a/screenshots/de/features.space.impl.root_SpaceView_Day_4_de.png b/screenshots/de/features.space.impl.root_SpaceView_Day_4_de.png new file mode 100644 index 0000000000..6b09249a34 --- /dev/null +++ b/screenshots/de/features.space.impl.root_SpaceView_Day_4_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7fa009eca8f6308faeca75f3abfdae4e1a2cb6835e4f21ce08eec3464f8be187 +size 55502 diff --git a/screenshots/de/features.space.impl.root_SpaceView_Day_5_de.png b/screenshots/de/features.space.impl.root_SpaceView_Day_5_de.png new file mode 100644 index 0000000000..1899ad0987 --- /dev/null +++ b/screenshots/de/features.space.impl.root_SpaceView_Day_5_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6c0d140d5bbf6d4478403b72bb379a0aedce3a71e212b38a65ff12053ac6030 +size 54644 diff --git a/screenshots/de/features.space.impl_SpaceView_Day_2_de.png b/screenshots/de/features.space.impl_SpaceView_Day_2_de.png deleted file mode 100644 index dfbfac62d4..0000000000 --- a/screenshots/de/features.space.impl_SpaceView_Day_2_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c300acd3609d5f5c2b9b641d84dff196c3d44973a270b89f12919dced5b1ff91 -size 47339 diff --git a/screenshots/de/features.space.impl_SpaceView_Day_3_de.png b/screenshots/de/features.space.impl_SpaceView_Day_3_de.png deleted file mode 100644 index 2823c4d3e3..0000000000 --- a/screenshots/de/features.space.impl_SpaceView_Day_3_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c471588072bc12721ae792976ddbbb756206aacd1e2bdfb91f58e79b3f458b1f -size 46103 diff --git a/screenshots/de/features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_de.png b/screenshots/de/features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_de.png index a0b071fdc4..9741448fa7 100644 --- a/screenshots/de/features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_de.png +++ b/screenshots/de/features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:583703525dc6b252fe7042d26b4ea3af06346eb971a78360db3a1fdf66ea995d -size 15110 +oid sha256:6d4b8d2bb20ecf61d7f288b25b542d3441335c765d47d21282a9464a591da12b +size 25133 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_0_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_0_de.png index f86f0e4348..f5576fd8a3 100644 --- a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_0_de.png +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a585cdbb479e4ab2aa53f9d0358a9a2068d2607094558f176a83bd2efcee48b -size 46009 +oid sha256:bcc6e14373fd9da583310dbbc759d0286a3b8ae36ddb0284709e3b937eb82d45 +size 48300 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_10_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_10_de.png index d7a3cd950d..88ce06753f 100644 --- a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_10_de.png +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0789c48566c0a7269eb85cd57cf91ed0e9709452a93a9441d5976c25951e737f -size 35821 +oid sha256:684222c49ca9fc06a9e63fcd46736b4c68b2f23f16e77228f78349f926514e6c +size 28094 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_1_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_1_de.png index f86f0e4348..f5576fd8a3 100644 --- a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_1_de.png +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a585cdbb479e4ab2aa53f9d0358a9a2068d2607094558f176a83bd2efcee48b -size 46009 +oid sha256:bcc6e14373fd9da583310dbbc759d0286a3b8ae36ddb0284709e3b937eb82d45 +size 48300 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_2_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_2_de.png index d119111de8..f07f83f945 100644 --- a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_2_de.png +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ad9081355cb2897a5bf759d6f73920f5d9a1ae6d0ff7025df7c1200c4f25ecda -size 40869 +oid sha256:92f2c1c5cac7875798293f1daff35c4753bc1aec81ec42101288bab269c633e2 +size 43345 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_4_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_4_de.png index e56aa1ca9f..aa41993ba1 100644 --- a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_4_de.png +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5615a84e510c1ee49321fba1bb2620042d32aa67d5197b8b88a642bf1c3edd9 -size 36232 +oid sha256:f309d9cdb5c9b5aec9f9736b045895c33a2db5873207c9b4f410b429e3cb4c2d +size 36239 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_5_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_5_de.png index 97c1810a62..095e794a2c 100644 --- a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_5_de.png +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e80a013181b42882444b9c56c7267f2fb7ceb9c4deb88c65470e9e45b8046f75 -size 52753 +oid sha256:066c64055bc1034d2451d9d812b2b48692260062364424d2de5be2eca81d3922 +size 52848 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_de.png index 2e44a3544d..a6b85f2af9 100644 --- a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_de.png +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d68b6dc0be99d386eaccc13e720b05669526779cb6cc809fab6c87d6096eeae5 -size 43820 +oid sha256:355c47c2dbed84f808637b0093e7245667ad2dbffeabf84ea7f1244d377beca6 +size 43916 diff --git a/screenshots/de/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_de.png b/screenshots/de/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_de.png index 39db3ce4fa..badc9d4606 100644 --- a/screenshots/de/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_de.png +++ b/screenshots/de/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f983de6f7ed75b02a491fb7d4a9341b40a1f367d1e298a77ff8baf9b475a75c3 -size 30032 +oid sha256:0c3c0a2ec93e0ccabcb1b529dfa6fb117ea572719670dec770a31789d32db131 +size 27229 diff --git a/screenshots/de/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_de.png b/screenshots/de/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_de.png index 97c1810a62..095e794a2c 100644 --- a/screenshots/de/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_de.png +++ b/screenshots/de/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e80a013181b42882444b9c56c7267f2fb7ceb9c4deb88c65470e9e45b8046f75 -size 52753 +oid sha256:066c64055bc1034d2451d9d812b2b48692260062364424d2de5be2eca81d3922 +size 52848 diff --git a/screenshots/de/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_de.png b/screenshots/de/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_de.png index 2e44a3544d..a6b85f2af9 100644 --- a/screenshots/de/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_de.png +++ b/screenshots/de/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d68b6dc0be99d386eaccc13e720b05669526779cb6cc809fab6c87d6096eeae5 -size 43820 +oid sha256:355c47c2dbed84f808637b0093e7245667ad2dbffeabf84ea7f1244d377beca6 +size 43916 diff --git a/screenshots/de/libraries.accountselect.impl_AccountSelectView_Day_0_de.png b/screenshots/de/libraries.accountselect.impl_AccountSelectView_Day_0_de.png new file mode 100644 index 0000000000..49108ddb0e --- /dev/null +++ b/screenshots/de/libraries.accountselect.impl_AccountSelectView_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6fb3f21b9b6460403f30709bc3464c8f584af34e7ac27083827a5c2c6121a2b1 +size 8243 diff --git a/screenshots/de/libraries.accountselect.impl_AccountSelectView_Day_1_de.png b/screenshots/de/libraries.accountselect.impl_AccountSelectView_Day_1_de.png new file mode 100644 index 0000000000..ce17201424 --- /dev/null +++ b/screenshots/de/libraries.accountselect.impl_AccountSelectView_Day_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf13a8c2d3b46a5f2e2b17b681b60ccc62dc886d137ec5464e35c80f4afe19fe +size 49022 diff --git a/screenshots/de/libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_de.png b/screenshots/de/libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_de.png index 3265fe531a..7e5de34316 100644 --- a/screenshots/de/libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_de.png +++ b/screenshots/de/libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bda85c0761b9ffe6b6cb21dffd5d5f2b0e7bbd3d9764210a4ee8bf042dba6efa -size 19839 +oid sha256:0095fd43138cfbc55518c6aeb4a9e578d2ea299e8f55c658e80120076402847d +size 18263 diff --git a/screenshots/de/libraries.matrix.ui.components_SpaceHeaderView_Day_0_de.png b/screenshots/de/libraries.matrix.ui.components_SpaceHeaderView_Day_0_de.png index df1ea23f0e..1f052f4c3d 100644 --- a/screenshots/de/libraries.matrix.ui.components_SpaceHeaderView_Day_0_de.png +++ b/screenshots/de/libraries.matrix.ui.components_SpaceHeaderView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be76e4bd66ecdb78bd45367112bc0b4f270374bd2bc7413ee70e346e80e4709c -size 62031 +oid sha256:0978f8363e8d5234bc19625156a572219a6cbab3573e2c024a1cf828d4446af9 +size 61237 diff --git a/screenshots/de/libraries.matrix.ui.components_SpaceInfoRow_Day_0_de.png b/screenshots/de/libraries.matrix.ui.components_SpaceInfoRow_Day_0_de.png index 003a777ab1..1407cfcd80 100644 --- a/screenshots/de/libraries.matrix.ui.components_SpaceInfoRow_Day_0_de.png +++ b/screenshots/de/libraries.matrix.ui.components_SpaceInfoRow_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd08df4edb77aff2685a75d4e024e7eff82cba57515e9787074386aa70379bd3 -size 23041 +oid sha256:20ab8a876550e5536c89b8b3dc7028444a9430694f3fc97522fef4d601ec2f87 +size 24582 diff --git a/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_0_de.png b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_0_de.png new file mode 100644 index 0000000000..738e01fe3f --- /dev/null +++ b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f7d03c199e22eb0f0b6e34b42efc65d138ac32c743631f65553ba3497ded0362 +size 17452 diff --git a/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_1_de.png b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_1_de.png new file mode 100644 index 0000000000..4f234befc6 --- /dev/null +++ b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:06ba4b65baf7638599ce0adefe25c3836c322168c5e0cdfe8f93162b2d3b3b8a +size 13462 diff --git a/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_2_de.png b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_2_de.png new file mode 100644 index 0000000000..1c3fc7c033 --- /dev/null +++ b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_2_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59335d0ddefd6d4b668ce0004c992ef24f34268e3155ac8c41fe9418c700bf2b +size 10782 diff --git a/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_3_de.png b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_3_de.png new file mode 100644 index 0000000000..388eaba735 --- /dev/null +++ b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_3_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96aef4e08d3d1d21701a8c18832f60eca0317c2ced0c4f06d8d9a71231434bbd +size 24579 diff --git a/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_4_de.png b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_4_de.png new file mode 100644 index 0000000000..866b8156d9 --- /dev/null +++ b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_4_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52beb69cc61ef5a526b371c28859e9fd290ef709d2ee01a60a5343f3397acf52 +size 19003 diff --git a/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_5_de.png b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_5_de.png new file mode 100644 index 0000000000..fa8d622303 --- /dev/null +++ b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_5_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c913b95850bcd97e39dc415bfd130eddacb4b22119998eb083217804ed530d4 +size 14892 diff --git a/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_6_de.png b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_6_de.png new file mode 100644 index 0000000000..96ab4eec7d --- /dev/null +++ b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_6_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7e79cc9e9d2cb0918afcc6ae3c4590b7d4d79ae3fc3d6480f88797ea93cf64b1 +size 34985 diff --git a/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_7_de.png b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_7_de.png new file mode 100644 index 0000000000..b74c0e675e --- /dev/null +++ b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_7_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ce56146c048dc9739a59564e6b53dfe8857f02489817a7e7931b00870b197b5 +size 39827 diff --git a/screenshots/de/libraries.troubleshoot.impl.history_PushHistoryView_Day_3_de.png b/screenshots/de/libraries.troubleshoot.impl.history_PushHistoryView_Day_3_de.png new file mode 100644 index 0000000000..f4c4da3dc5 --- /dev/null +++ b/screenshots/de/libraries.troubleshoot.impl.history_PushHistoryView_Day_3_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2fe5fd358af75454841c8648f64bdece8225e4327f5ec0d3e112d7b38da97fb1 +size 23951 diff --git a/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_de.png b/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_de.png index 912fd2764a..3b828cfa21 100644 --- a/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_de.png +++ b/screenshots/de/libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95cbb510c69b24bc457039cdeda49846f5d547d73676e2f74e2bdf722654d25e -size 20841 +oid sha256:4143aa80f5e04d2eb1a150bcf2ffd54eee46662e2d142bf4931b7ec2e877a913 +size 25803 diff --git a/screenshots/html/data.js b/screenshots/html/data.js index d4e0ce58b9..2bdbeece94 100644 --- a/screenshots/html/data.js +++ b/screenshots/html/data.js @@ -1,77 +1,79 @@ // Generated file, do not edit export const screenshots = [ ["en","en-dark","de",], -["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20350,], +["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20369,], ["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_0_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_0_en",0,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20350,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20350,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20350,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20350,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20350,], -["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20350,], -["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20350,], -["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20350,], -["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20350,], -["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20350,], -["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20350,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20369,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20369,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20369,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20369,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20369,], +["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20369,], +["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20369,], +["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20369,], +["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20369,], +["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20369,], +["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20369,], ["features.login.impl.accountprovider_AccountProviderView_Day_0_en","features.login.impl.accountprovider_AccountProviderView_Night_0_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_1_en","features.login.impl.accountprovider_AccountProviderView_Night_1_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_2_en","features.login.impl.accountprovider_AccountProviderView_Night_2_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_3_en","features.login.impl.accountprovider_AccountProviderView_Night_3_en",0,], +["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20369,], +["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20369,], ["features.messages.impl.actionlist_ActionListViewContent_Day_0_en","features.messages.impl.actionlist_ActionListViewContent_Night_0_en",0,], -["features.messages.impl.actionlist_ActionListViewContent_Day_10_en","features.messages.impl.actionlist_ActionListViewContent_Night_10_en",20350,], -["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20350,], -["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20350,], +["features.messages.impl.actionlist_ActionListViewContent_Day_10_en","features.messages.impl.actionlist_ActionListViewContent_Night_10_en",20369,], +["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20369,], +["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20369,], ["features.messages.impl.actionlist_ActionListViewContent_Day_1_en","features.messages.impl.actionlist_ActionListViewContent_Night_1_en",0,], -["features.messages.impl.actionlist_ActionListViewContent_Day_2_en","features.messages.impl.actionlist_ActionListViewContent_Night_2_en",20350,], -["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20350,], -["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20350,], -["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20350,], -["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20350,], -["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20350,], -["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20350,], -["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20350,], -["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20350,], -["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20350,], -["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20350,], -["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20350,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20350,], -["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20350,], -["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20350,], -["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20350,], -["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20350,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20350,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20350,], -["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20350,], +["features.messages.impl.actionlist_ActionListViewContent_Day_2_en","features.messages.impl.actionlist_ActionListViewContent_Night_2_en",20369,], +["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20369,], +["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20369,], +["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20369,], +["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20369,], +["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20369,], +["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20369,], +["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20369,], +["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20369,], +["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20369,], +["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20369,], +["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20369,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20369,], +["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20369,], +["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20369,], +["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20369,], +["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20369,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20369,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20369,], +["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20369,], ["libraries.designsystem.components_Announcement_Day_0_en","libraries.designsystem.components_Announcement_Night_0_en",0,], -["services.apperror.impl_AppErrorView_Day_0_en","services.apperror.impl_AppErrorView_Night_0_en",20350,], +["services.apperror.impl_AppErrorView_Day_0_en","services.apperror.impl_AppErrorView_Night_0_en",20369,], ["libraries.designsystem.components.async_AsyncActionView_Day_0_en","libraries.designsystem.components.async_AsyncActionView_Night_0_en",0,], -["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",20350,], +["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",20369,], ["libraries.designsystem.components.async_AsyncActionView_Day_2_en","libraries.designsystem.components.async_AsyncActionView_Night_2_en",0,], -["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",20350,], +["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",20369,], ["libraries.designsystem.components.async_AsyncActionView_Day_4_en","libraries.designsystem.components.async_AsyncActionView_Night_4_en",0,], -["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",20350,], +["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",20369,], ["libraries.designsystem.components.async_AsyncIndicatorFailure_Day_0_en","libraries.designsystem.components.async_AsyncIndicatorFailure_Night_0_en",0,], ["libraries.designsystem.components.async_AsyncIndicatorLoading_Day_0_en","libraries.designsystem.components.async_AsyncIndicatorLoading_Night_0_en",0,], ["libraries.designsystem.components.async_AsyncLoading_Day_0_en","libraries.designsystem.components.async_AsyncLoading_Night_0_en",0,], -["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",20350,], +["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",20369,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_0_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_0_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_1_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_1_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_2_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_2_en",0,], @@ -81,19 +83,19 @@ export const screenshots = [ ["libraries.matrix.ui.components_AttachmentThumbnail_Day_6_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_6_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_7_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_7_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_8_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_8_en",0,], -["features.messages.impl.attachments.preview_AttachmentsView_0_en","",20350,], -["features.messages.impl.attachments.preview_AttachmentsView_1_en","",20350,], -["features.messages.impl.attachments.preview_AttachmentsView_2_en","",20350,], -["features.messages.impl.attachments.preview_AttachmentsView_3_en","",20350,], -["features.messages.impl.attachments.preview_AttachmentsView_4_en","",20350,], -["features.messages.impl.attachments.preview_AttachmentsView_5_en","",20350,], -["features.messages.impl.attachments.preview_AttachmentsView_6_en","",20350,], -["features.messages.impl.attachments.preview_AttachmentsView_7_en","",20350,], -["features.messages.impl.attachments.preview_AttachmentsView_8_en","",20350,], +["features.messages.impl.attachments.preview_AttachmentsView_0_en","",20369,], +["features.messages.impl.attachments.preview_AttachmentsView_1_en","",20369,], +["features.messages.impl.attachments.preview_AttachmentsView_2_en","",20369,], +["features.messages.impl.attachments.preview_AttachmentsView_3_en","",20369,], +["features.messages.impl.attachments.preview_AttachmentsView_4_en","",20369,], +["features.messages.impl.attachments.preview_AttachmentsView_5_en","",20369,], +["features.messages.impl.attachments.preview_AttachmentsView_6_en","",20369,], +["features.messages.impl.attachments.preview_AttachmentsView_7_en","",20369,], +["features.messages.impl.attachments.preview_AttachmentsView_8_en","",20369,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_1_en",0,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_2_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_2_en",0,], -["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20350,], +["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20369,], ["libraries.designsystem.components.avatar.internal_AvatarCluster_Avatars_en","",0,], ["libraries.designsystem.components.avatar_AvatarRowLastOnTopRtl_Day_0_en","libraries.designsystem.components.avatar_AvatarRowLastOnTopRtl_Night_0_en",0,], ["libraries.designsystem.components.avatar_AvatarRowLastOnTopRtl_Day_1_en","libraries.designsystem.components.avatar_AvatarRowLastOnTopRtl_Night_1_en",0,], @@ -121,7 +123,22 @@ export const screenshots = [ ["libraries.designsystem.components.avatar_Avatar_Avatars_102_en","",0,], ["libraries.designsystem.components.avatar_Avatar_Avatars_103_en","",0,], ["libraries.designsystem.components.avatar_Avatar_Avatars_104_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_105_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_106_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_107_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_108_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_109_en","",0,], ["libraries.designsystem.components.avatar_Avatar_Avatars_10_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_110_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_111_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_112_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_113_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_114_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_115_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_116_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_117_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_118_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_119_en","",0,], ["libraries.designsystem.components.avatar_Avatar_Avatars_11_en","",0,], ["libraries.designsystem.components.avatar_Avatar_Avatars_12_en","",0,], ["libraries.designsystem.components.avatar_Avatar_Avatars_13_en","",0,], @@ -225,145 +242,147 @@ export const screenshots = [ ["libraries.designsystem.modifiers_BackgroundVerticalGradientEnterprise_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradientEnterprise_Night_0_en",0,], ["libraries.designsystem.modifiers_BackgroundVerticalGradient_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradient_Night_0_en",0,], ["libraries.designsystem.components_Badge_Day_0_en","libraries.designsystem.components_Badge_Night_0_en",0,], -["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20350,], +["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20369,], +["libraries.designsystem.atomic.atoms_BetaLabel_Day_0_en","libraries.designsystem.atomic.atoms_BetaLabel_Night_0_en",0,], ["libraries.designsystem.components_BigIcon_Day_0_en","libraries.designsystem.components_BigIcon_Night_0_en",0,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20350,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20350,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20350,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20350,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20350,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20350,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20350,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20369,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20369,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20369,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20369,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20369,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20369,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20369,], ["libraries.designsystem.theme.components_BottomSheetDragHandle_Day_0_en","libraries.designsystem.theme.components_BottomSheetDragHandle_Night_0_en",0,], -["features.rageshake.impl.bugreport_BugReportView_Day_0_en","features.rageshake.impl.bugreport_BugReportView_Night_0_en",20350,], -["features.rageshake.impl.bugreport_BugReportView_Day_1_en","features.rageshake.impl.bugreport_BugReportView_Night_1_en",20350,], -["features.rageshake.impl.bugreport_BugReportView_Day_2_en","features.rageshake.impl.bugreport_BugReportView_Night_2_en",20350,], -["features.rageshake.impl.bugreport_BugReportView_Day_3_en","features.rageshake.impl.bugreport_BugReportView_Night_3_en",20350,], -["features.rageshake.impl.bugreport_BugReportView_Day_4_en","features.rageshake.impl.bugreport_BugReportView_Night_4_en",20350,], +["features.rageshake.impl.bugreport_BugReportView_Day_0_en","features.rageshake.impl.bugreport_BugReportView_Night_0_en",20369,], +["features.rageshake.impl.bugreport_BugReportView_Day_1_en","features.rageshake.impl.bugreport_BugReportView_Night_1_en",20369,], +["features.rageshake.impl.bugreport_BugReportView_Day_2_en","features.rageshake.impl.bugreport_BugReportView_Night_2_en",20369,], +["features.rageshake.impl.bugreport_BugReportView_Day_3_en","features.rageshake.impl.bugreport_BugReportView_Night_3_en",20369,], +["features.rageshake.impl.bugreport_BugReportView_Day_4_en","features.rageshake.impl.bugreport_BugReportView_Night_4_en",20369,], ["libraries.designsystem.atomic.molecules_ButtonColumnMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ButtonColumnMolecule_Night_0_en",0,], ["libraries.designsystem.atomic.molecules_ButtonRowMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ButtonRowMolecule_Night_0_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_0_en","features.messages.impl.timeline.components_CallMenuItem_Night_0_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_1_en","features.messages.impl.timeline.components_CallMenuItem_Night_1_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_2_en","features.messages.impl.timeline.components_CallMenuItem_Night_2_en",20350,], -["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20350,], +["features.messages.impl.timeline.components_CallMenuItem_Day_2_en","features.messages.impl.timeline.components_CallMenuItem_Night_2_en",20369,], +["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20369,], ["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_5_en","features.messages.impl.timeline.components_CallMenuItem_Night_5_en",0,], ["features.call.impl.ui_CallScreenView_Day_0_en","features.call.impl.ui_CallScreenView_Night_0_en",0,], -["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20350,], -["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20350,], -["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20350,], -["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20350,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20350,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20350,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_0_en","features.changeroommemberroles.impl_ChangeRolesView_Night_0_en",20350,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_10_en","features.changeroommemberroles.impl_ChangeRolesView_Night_10_en",20350,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_11_en","features.changeroommemberroles.impl_ChangeRolesView_Night_11_en",20350,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_12_en","features.changeroommemberroles.impl_ChangeRolesView_Night_12_en",20350,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_1_en","features.changeroommemberroles.impl_ChangeRolesView_Night_1_en",20350,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_2_en","features.changeroommemberroles.impl_ChangeRolesView_Night_2_en",20350,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_3_en","features.changeroommemberroles.impl_ChangeRolesView_Night_3_en",20350,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_4_en","features.changeroommemberroles.impl_ChangeRolesView_Night_4_en",20350,], +["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20369,], +["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20369,], +["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20369,], +["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20369,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20369,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20369,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_0_en","features.changeroommemberroles.impl_ChangeRolesView_Night_0_en",20369,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_10_en","features.changeroommemberroles.impl_ChangeRolesView_Night_10_en",20369,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_11_en","features.changeroommemberroles.impl_ChangeRolesView_Night_11_en",20369,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_12_en","features.changeroommemberroles.impl_ChangeRolesView_Night_12_en",20369,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_1_en","features.changeroommemberroles.impl_ChangeRolesView_Night_1_en",20369,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_2_en","features.changeroommemberroles.impl_ChangeRolesView_Night_2_en",20369,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_3_en","features.changeroommemberroles.impl_ChangeRolesView_Night_3_en",20369,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_4_en","features.changeroommemberroles.impl_ChangeRolesView_Night_4_en",20369,], ["features.changeroommemberroles.impl_ChangeRolesView_Day_5_en","features.changeroommemberroles.impl_ChangeRolesView_Night_5_en",0,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_6_en","features.changeroommemberroles.impl_ChangeRolesView_Night_6_en",20350,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_7_en","features.changeroommemberroles.impl_ChangeRolesView_Night_7_en",20350,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_8_en","features.changeroommemberroles.impl_ChangeRolesView_Night_8_en",20350,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_9_en","features.changeroommemberroles.impl_ChangeRolesView_Night_9_en",20350,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en",20350,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en",20350,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en",20350,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en",20350,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en",20350,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en",20350,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en",20350,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_6_en","features.changeroommemberroles.impl_ChangeRolesView_Night_6_en",20369,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_7_en","features.changeroommemberroles.impl_ChangeRolesView_Night_7_en",20369,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_8_en","features.changeroommemberroles.impl_ChangeRolesView_Night_8_en",20369,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_9_en","features.changeroommemberroles.impl_ChangeRolesView_Night_9_en",20369,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en",20369,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en",20369,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en",20369,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en",20369,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en",20369,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en",20369,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en",20369,], ["features.login.impl.changeserver_ChangeServerView_Day_0_en","features.login.impl.changeserver_ChangeServerView_Night_0_en",0,], -["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",20350,], -["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20350,], -["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20350,], -["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20350,], +["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",20369,], +["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20369,], +["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20369,], +["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20369,], ["libraries.matrix.ui.components_CheckableResolvedUserRow_en","",0,], -["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20350,], +["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20369,], ["libraries.designsystem.theme.components_Checkboxes_Toggles_en","",0,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20350,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20350,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20350,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20350,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20350,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20350,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20350,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20369,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20369,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20369,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20369,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20369,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20369,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20369,], ["libraries.designsystem.theme.components_CircularProgressIndicator_Progress_Indicators_en","",0,], ["libraries.designsystem.components_ClickableLinkText_Text_en","",0,], ["libraries.designsystem.theme_ColorAliases_Day_0_en","libraries.designsystem.theme_ColorAliases_Night_0_en",0,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20350,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20350,], -["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20350,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20369,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20369,], +["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20369,], ["libraries.textcomposer_ComposerModeView_Day_1_en","libraries.textcomposer_ComposerModeView_Night_1_en",0,], ["libraries.textcomposer_ComposerModeView_Day_2_en","libraries.textcomposer_ComposerModeView_Night_2_en",0,], ["libraries.textcomposer_ComposerModeView_Day_3_en","libraries.textcomposer_ComposerModeView_Night_3_en",0,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20350,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20350,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20350,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20350,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20350,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20350,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20350,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20350,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20350,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20350,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20350,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20350,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20350,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20350,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20350,], -["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20350,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20369,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20369,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20369,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20369,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20369,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20369,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20369,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20369,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20369,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20369,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20369,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20369,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20369,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20369,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20369,], +["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20369,], ["libraries.designsystem.components.dialogs_ConfirmationDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_ConfirmationDialog_Day_0_en","libraries.designsystem.components.dialogs_ConfirmationDialog_Night_0_en",0,], ["features.networkmonitor.api.ui_ConnectivityIndicatorView_Day_0_en","features.networkmonitor.api.ui_ConnectivityIndicatorView_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_CounterAtom_Day_0_en","libraries.designsystem.atomic.atoms_CounterAtom_Night_0_en",0,], -["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20350,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20350,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20350,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20350,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20350,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20350,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20350,], -["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20350,], -["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20350,], -["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20350,], -["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20350,], -["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20350,], -["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20350,], -["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20350,], -["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20350,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20350,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20350,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20350,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20350,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20350,], +["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20369,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20369,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20369,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20369,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20369,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20369,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20369,], +["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20369,], +["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20369,], +["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20369,], +["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20369,], +["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20369,], +["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20369,], +["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20369,], +["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20369,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20369,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20369,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20369,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20369,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20369,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_1_en",0,], -["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20350,], -["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20350,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20350,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20350,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20350,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20350,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20350,], +["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20369,], +["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20369,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20369,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20369,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20369,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20369,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20369,], ["features.logout.impl.direct_DefaultDirectLogoutView_Day_0_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_0_en",0,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",20350,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20350,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20350,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",20369,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20369,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20369,], ["features.logout.impl.direct_DefaultDirectLogoutView_Day_4_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_4_en",0,], -["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",20350,], -["features.home.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en","features.home.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en",20350,], -["features.home.impl.components_DefaultRoomListTopBar_Day_0_en","features.home.impl.components_DefaultRoomListTopBar_Night_0_en",20350,], +["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",20369,], +["features.home.impl.components_DefaultRoomListTopBarMultiAccount_Day_0_en","features.home.impl.components_DefaultRoomListTopBarMultiAccount_Night_0_en",20369,], +["features.home.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en","features.home.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en",20369,], +["features.home.impl.components_DefaultRoomListTopBar_Day_0_en","features.home.impl.components_DefaultRoomListTopBar_Night_0_en",20369,], ["features.licenses.impl.details_DependenciesDetailsView_Day_0_en","features.licenses.impl.details_DependenciesDetailsView_Night_0_en",0,], -["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",20350,], -["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20350,], -["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20350,], -["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20350,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20350,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20350,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20350,], +["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",20369,], +["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20369,], +["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20369,], +["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20369,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20369,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20369,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20369,], ["libraries.designsystem.theme.components_DialogWithDestructiveButton_Dialog_with_destructive_button_Dialogs_en","",0,], ["libraries.designsystem.theme.components_DialogWithOnlyMessageAndOkButton_Dialog_with_only_message_and_ok_button_Dialogs_en","",0,], ["libraries.designsystem.theme.components_DialogWithThirdButton_Dialog_with_third_button_Dialogs_en","",0,], @@ -378,18 +397,18 @@ export const screenshots = [ ["libraries.designsystem.text_DpScale_1_0f__en","",0,], ["libraries.designsystem.text_DpScale_1_5f__en","",0,], ["libraries.designsystem.theme.components_DropdownMenuItem_Menus_en","",0,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20350,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20350,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20350,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20350,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20350,], -["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_0_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_0_en",20350,], -["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_1_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_1_en",20350,], -["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_2_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_2_en",20350,], -["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_3_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_3_en",20350,], -["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_4_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_4_en",20350,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20350,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20350,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20369,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20369,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20369,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20369,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20369,], +["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_0_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_0_en",20369,], +["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_1_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_1_en",20369,], +["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_2_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_2_en",20369,], +["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_3_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_3_en",20369,], +["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_4_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_4_en",20369,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20369,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20369,], ["libraries.matrix.ui.components_EditableAvatarView_Day_0_en","libraries.matrix.ui.components_EditableAvatarView_Night_0_en",0,], ["libraries.matrix.ui.components_EditableAvatarView_Day_1_en","libraries.matrix.ui.components_EditableAvatarView_Night_1_en",0,], ["libraries.matrix.ui.components_EditableAvatarView_Day_2_en","libraries.matrix.ui.components_EditableAvatarView_Night_2_en",0,], @@ -400,13 +419,14 @@ export const screenshots = [ ["libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Night_0_en",0,], ["features.messages.impl.timeline.components.customreaction_EmojiItem_Day_0_en","features.messages.impl.timeline.components.customreaction_EmojiItem_Night_0_en",0,], -["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en",20350,], -["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_1_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_1_en",20350,], +["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en",20369,], +["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_1_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_1_en",20369,], ["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_2_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_2_en",0,], ["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_3_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_3_en",0,], -["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20350,], -["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20350,], -["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20350,], +["libraries.ui.common.nodes_EmptyView_Day_0_en","libraries.ui.common.nodes_EmptyView_Night_0_en",0,], +["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20369,], +["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20369,], +["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20369,], ["features.messages.impl.timeline.debug_EventDebugInfoView_Day_0_en","features.messages.impl.timeline.debug_EventDebugInfoView_Night_0_en",0,], ["libraries.designsystem.components_ExpandableBottomSheetLayout_en","",0,], ["libraries.featureflag.ui_FeatureListView_Day_0_en","libraries.featureflag.ui_FeatureListView_Night_0_en",0,], @@ -425,41 +445,41 @@ export const screenshots = [ ["libraries.designsystem.theme.components_FloatingActionButton_Floating_Action_Buttons_en","",0,], ["libraries.designsystem.atomic.pages_FlowStepPage_Day_0_en","libraries.designsystem.atomic.pages_FlowStepPage_Night_0_en",0,], ["features.messages.impl.timeline.focus_FocusRequestStateView_Day_0_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_0_en",0,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20350,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20350,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20350,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20369,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20369,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20369,], ["features.messages.impl.timeline.components_FocusedEventEnterprise_Day_0_en","features.messages.impl.timeline.components_FocusedEventEnterprise_Night_0_en",0,], ["features.messages.impl.timeline.components_FocusedEvent_Day_0_en","features.messages.impl.timeline.components_FocusedEvent_Night_0_en",0,], ["libraries.textcomposer.components_FormattingOption_Day_0_en","libraries.textcomposer.components_FormattingOption_Night_0_en",0,], ["features.messages.impl.forward_ForwardMessagesView_Day_0_en","features.messages.impl.forward_ForwardMessagesView_Night_0_en",0,], ["features.messages.impl.forward_ForwardMessagesView_Day_1_en","features.messages.impl.forward_ForwardMessagesView_Night_1_en",0,], ["features.messages.impl.forward_ForwardMessagesView_Day_2_en","features.messages.impl.forward_ForwardMessagesView_Night_2_en",0,], -["features.messages.impl.forward_ForwardMessagesView_Day_3_en","features.messages.impl.forward_ForwardMessagesView_Night_3_en",20350,], -["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20350,], +["features.messages.impl.forward_ForwardMessagesView_Day_3_en","features.messages.impl.forward_ForwardMessagesView_Night_3_en",20369,], +["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20369,], ["libraries.designsystem.components.button_GradientFloatingActionButtonCircleShape_Day_0_en","libraries.designsystem.components.button_GradientFloatingActionButtonCircleShape_Night_0_en",0,], ["libraries.designsystem.components.button_GradientFloatingActionButton_Day_0_en","libraries.designsystem.components.button_GradientFloatingActionButton_Night_0_en",0,], ["features.messages.impl.timeline.components.group_GroupHeaderView_Day_0_en","features.messages.impl.timeline.components.group_GroupHeaderView_Night_0_en",0,], ["libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Night_0_en",0,], ["libraries.designsystem.atomic.pages_HeaderFooterPage_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPage_Night_0_en",0,], -["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20350,], -["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20350,], +["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20369,], +["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20369,], ["features.home.impl_HomeViewA11y_en","",0,], -["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20350,], -["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20350,], +["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20369,], +["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20369,], ["features.home.impl_HomeView_Day_11_en","features.home.impl_HomeView_Night_11_en",0,], ["features.home.impl_HomeView_Day_12_en","features.home.impl_HomeView_Night_12_en",0,], -["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20350,], -["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20350,], -["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20350,], -["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20350,], -["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20350,], -["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20350,], -["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20350,], -["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20350,], -["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20350,], -["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20350,], -["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20350,], -["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20350,], +["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20369,], +["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20369,], +["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20369,], +["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20369,], +["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20369,], +["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20369,], +["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20369,], +["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20369,], +["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20369,], +["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20369,], +["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20369,], +["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20369,], ["libraries.designsystem.theme.components_HorizontalDivider_Dividers_en","",0,], ["libraries.designsystem.ruler_HorizontalRuler_Day_0_en","libraries.designsystem.ruler_HorizontalRuler_Night_0_en",0,], ["libraries.designsystem.theme.components_IconButton_Buttons_en","",0,], @@ -478,8 +498,8 @@ export const screenshots = [ ["libraries.designsystem.icons_IconsCompound_Day_5_en","libraries.designsystem.icons_IconsCompound_Night_5_en",0,], ["libraries.designsystem.icons_IconsOther_Day_0_en","libraries.designsystem.icons_IconsOther_Night_0_en",0,], ["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_0_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_0_en",0,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20350,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20350,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20369,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20369,], ["libraries.mediaviewer.impl.gallery.ui_ImageItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_ImageItemView_Night_0_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_0_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_0_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_10_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_10_en",0,], @@ -487,97 +507,110 @@ export const screenshots = [ ["libraries.matrix.ui.messages.reply_InReplyToView_Day_1_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_1_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_2_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_2_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_3_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_3_en",0,], -["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20350,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20369,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_5_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_5_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_6_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_6_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_7_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_7_en",0,], -["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",20350,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",20369,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_9_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_9_en",0,], -["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",20350,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20350,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20350,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20350,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20350,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20350,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20350,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20350,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20350,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20350,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20350,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20350,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20350,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20350,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20350,], +["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",20369,], +["features.verifysession.impl.incoming_IncomingVerificationViewA11y_en","",0,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20369,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20369,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20369,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20369,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20369,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20369,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20369,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20369,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20369,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20369,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20369,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20369,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20369,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20369,], ["features.networkmonitor.api.ui_Indicator_Day_0_en","features.networkmonitor.api.ui_Indicator_Night_0_en",0,], ["libraries.designsystem.atomic.molecules_InfoListItemMolecule_Day_0_en","libraries.designsystem.atomic.molecules_InfoListItemMolecule_Night_0_en",0,], ["libraries.designsystem.atomic.organisms_InfoListOrganism_Day_0_en","libraries.designsystem.atomic.organisms_InfoListOrganism_Night_0_en",0,], ["libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Day_0_en","libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Night_0_en",0,], -["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20350,], -["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20350,], -["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20350,], +["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20369,], +["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20369,], +["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20369,], ["features.invitepeople.impl_InvitePeopleView_Day_2_en","features.invitepeople.impl_InvitePeopleView_Night_2_en",0,], ["features.invitepeople.impl_InvitePeopleView_Day_3_en","features.invitepeople.impl_InvitePeopleView_Night_3_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20350,], -["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20350,], -["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20350,], -["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20350,], +["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20369,], +["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20369,], +["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20369,], +["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20369,], ["features.invitepeople.impl_InvitePeopleView_Day_8_en","features.invitepeople.impl_InvitePeopleView_Night_8_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20350,], -["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20350,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20350,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20350,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20350,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20350,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20350,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20350,], +["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20369,], +["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20369,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20369,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20369,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20369,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20369,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20369,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20369,], ["features.joinroom.impl_JoinRoomView_Day_0_en","features.joinroom.impl_JoinRoomView_Night_0_en",0,], -["features.joinroom.impl_JoinRoomView_Day_10_en","features.joinroom.impl_JoinRoomView_Night_10_en",20350,], -["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20350,], -["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20350,], -["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20350,], -["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20350,], -["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20350,], -["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20350,], -["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20350,], -["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20350,], -["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20350,], -["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20350,], -["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20350,], -["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20350,], -["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20350,], -["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20350,], -["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20350,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20350,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20350,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20350,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20350,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20350,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20350,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20350,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20350,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20350,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20350,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20350,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20350,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20350,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20350,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20350,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20350,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20350,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20350,], +["features.joinroom.impl_JoinRoomView_Day_10_en","features.joinroom.impl_JoinRoomView_Night_10_en",20369,], +["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20369,], +["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20369,], +["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20369,], +["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20369,], +["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20369,], +["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20369,], +["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20369,], +["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20369,], +["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20369,], +["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20369,], +["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20369,], +["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20369,], +["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20369,], +["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20369,], +["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20369,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20369,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20369,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20369,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20369,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20369,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20369,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20369,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20369,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20369,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20369,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20369,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20369,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20369,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20369,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20369,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20369,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20369,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20369,], ["libraries.designsystem.components_LabelledCheckbox_Toggles_en","",0,], +["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20369,], +["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20369,], ["features.leaveroom.impl_LeaveRoomView_Day_0_en","features.leaveroom.impl_LeaveRoomView_Night_0_en",0,], -["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20350,], -["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20350,], -["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20350,], -["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20350,], -["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20350,], -["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20350,], -["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20350,], +["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20369,], +["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20369,], +["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20369,], +["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20369,], +["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20369,], +["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20369,], +["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20369,], +["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20369,], +["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20369,], +["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20369,], +["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20369,], +["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20369,], +["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20369,], +["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20369,], +["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20369,], +["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20369,], +["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20369,], ["libraries.designsystem.background_LightGradientBackground_Day_0_en","libraries.designsystem.background_LightGradientBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_LinearProgressIndicator_Progress_Indicators_en","",0,], ["features.messages.impl.link_LinkView_Day_0_en","features.messages.impl.link_LinkView_Night_0_en",0,], -["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20350,], +["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20369,], ["libraries.designsystem.components.dialogs_ListDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_ListDialog_Day_0_en","libraries.designsystem.components.dialogs_ListDialog_Night_0_en",0,], ["libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List_item_-_Primary_action_&_Icon_List_items_en","",0,], @@ -632,36 +665,37 @@ export const screenshots = [ ["libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List_supporting_text_-_small_padding_List_sections_en","",0,], ["libraries.textcomposer.components_LiveWaveformView_Day_0_en","libraries.textcomposer.components_LiveWaveformView_Night_0_en",0,], ["appnav.room.joined_LoadingRoomNodeView_Day_0_en","appnav.room.joined_LoadingRoomNodeView_Night_0_en",0,], -["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20350,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20350,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20350,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20350,], +["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20369,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20369,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20369,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20369,], ["appnav.loggedin_LoggedInView_Day_0_en","appnav.loggedin_LoggedInView_Night_0_en",0,], -["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",20350,], -["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20350,], -["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20350,], -["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20350,], -["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20350,], -["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20350,], -["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20350,], -["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20350,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20350,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20350,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20350,], -["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20350,], -["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20350,], -["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20350,], -["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20350,], -["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20350,], -["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20350,], -["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20350,], -["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20350,], -["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20350,], -["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20350,], -["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20350,], -["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20350,], +["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",20369,], +["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20369,], +["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20369,], +["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20369,], +["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20369,], +["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20369,], +["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20369,], +["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20369,], +["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20369,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20369,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20369,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20369,], +["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20369,], +["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20369,], +["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20369,], +["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20369,], +["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20369,], +["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20369,], +["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20369,], +["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20369,], +["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20369,], +["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20369,], +["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20369,], +["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20369,], ["libraries.designsystem.components.button_MainActionButton_Buttons_en","",0,], -["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20350,], +["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20369,], ["libraries.textcomposer.components.markdown_MarkdownTextInput_Day_0_en","libraries.textcomposer.components.markdown_MarkdownTextInput_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Night_0_en",0,], @@ -674,22 +708,22 @@ export const screenshots = [ ["libraries.matrix.ui.components_MatrixUserRow_Day_1_en","libraries.matrix.ui.components_MatrixUserRow_Night_1_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_1_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_1_en",0,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20350,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20350,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20369,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20369,], ["libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en",0,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20350,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20350,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20350,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20350,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20350,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20350,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20350,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20350,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20350,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20350,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20350,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20350,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20350,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20369,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20369,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20369,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20369,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20369,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20369,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20369,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20369,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20369,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20369,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20369,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20369,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20369,], ["libraries.mediaviewer.impl.local.image_MediaImageView_Day_0_en","libraries.mediaviewer.impl.local.image_MediaImageView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_0_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_1_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_1_en",0,], @@ -697,14 +731,14 @@ export const screenshots = [ ["libraries.mediaviewer.impl.local.video_MediaVideoView_Day_0_en","libraries.mediaviewer.impl.local.video_MediaVideoView_Night_0_en",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20350,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20350,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20369,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20369,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20350,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20369,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_1_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20350,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20369,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_5_en","",0,], @@ -718,7 +752,7 @@ export const screenshots = [ ["libraries.textcomposer.mentions_MentionSpanTheme_Day_0_en","libraries.textcomposer.mentions_MentionSpanTheme_Night_0_en",0,], ["libraries.designsystem.theme.components.previews_Menu_Menus_en","",0,], ["features.messages.impl.messagecomposer_MessageComposerViewVoice_Day_0_en","features.messages.impl.messagecomposer_MessageComposerViewVoice_Night_0_en",0,], -["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",20350,], +["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",20369,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_0_en","features.messages.impl.timeline.components_MessageEventBubble_Night_0_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_1_en","features.messages.impl.timeline.components_MessageEventBubble_Night_1_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_2_en","features.messages.impl.timeline.components_MessageEventBubble_Night_2_en",0,], @@ -727,7 +761,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessageEventBubble_Day_5_en","features.messages.impl.timeline.components_MessageEventBubble_Night_5_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_6_en","features.messages.impl.timeline.components_MessageEventBubble_Night_6_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_7_en","features.messages.impl.timeline.components_MessageEventBubble_Night_7_en",0,], -["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20350,], +["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20369,], ["features.messages.impl.timeline.components_MessageStateEventContainer_Day_0_en","features.messages.impl.timeline.components_MessageStateEventContainer_Night_0_en",0,], ["features.messages.impl.timeline.components_MessagesReactionButtonAdd_Day_0_en","features.messages.impl.timeline.components_MessagesReactionButtonAdd_Night_0_en",0,], ["features.messages.impl.timeline.components_MessagesReactionButtonExtra_Day_0_en","features.messages.impl.timeline.components_MessagesReactionButtonExtra_Night_0_en",0,], @@ -735,139 +769,138 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessagesReactionButton_Day_1_en","features.messages.impl.timeline.components_MessagesReactionButton_Night_1_en",0,], ["features.messages.impl.timeline.components_MessagesReactionButton_Day_2_en","features.messages.impl.timeline.components_MessagesReactionButton_Night_2_en",0,], ["features.messages.impl.timeline.components_MessagesReactionButton_Day_3_en","features.messages.impl.timeline.components_MessagesReactionButton_Night_3_en",0,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20350,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20350,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20350,], -["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20350,], -["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20350,], -["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20350,], -["features.messages.impl_MessagesView_Day_12_en","features.messages.impl_MessagesView_Night_12_en",20350,], -["features.messages.impl_MessagesView_Day_13_en","features.messages.impl_MessagesView_Night_13_en",20350,], -["features.messages.impl_MessagesView_Day_14_en","features.messages.impl_MessagesView_Night_14_en",20350,], -["features.messages.impl_MessagesView_Day_15_en","features.messages.impl_MessagesView_Night_15_en",20350,], -["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20350,], -["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20350,], -["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20350,], -["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20350,], -["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20350,], -["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20350,], -["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20350,], -["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20350,], -["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20350,], +["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20369,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20369,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20369,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20369,], +["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20369,], +["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20369,], +["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20369,], +["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20369,], +["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20369,], +["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20369,], +["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20369,], +["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20369,], +["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20369,], +["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20369,], +["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20369,], ["features.migration.impl_MigrationView_Day_0_en","features.migration.impl_MigrationView_Night_0_en",0,], -["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",20350,], +["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",20369,], ["libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en","",0,], ["libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en","",0,], ["appicon.element_MonochromeIcon_en","",0,], +["features.preferences.impl.root_MultiAccountSection_Day_0_en","features.preferences.impl.root_MultiAccountSection_Night_0_en",20369,], ["libraries.designsystem.components.dialogs_MultipleSelectionDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_MultipleSelectionDialog_Day_0_en","libraries.designsystem.components.dialogs_MultipleSelectionDialog_Night_0_en",0,], ["libraries.designsystem.components.list_MutipleSelectionListItemSelectedTrailingContent_Multiple_selection_List_item_-_selection_in_trailing_content_List_items_en","",0,], ["libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple_selection_List_item_-_selection_in_supporting_text_List_items_en","",0,], ["libraries.designsystem.components.list_MutipleSelectionListItem_Multiple_selection_List_item_-_no_selection_List_items_en","",0,], ["libraries.designsystem.theme.components_NavigationBar_App_Bars_en","",0,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20350,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20350,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20350,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20350,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20350,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20350,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20350,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20350,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20350,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20350,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20350,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20350,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20350,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20350,], -["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20350,], +["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20369,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20369,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20369,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20369,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20369,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20369,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20369,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20369,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20369,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20369,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20369,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20369,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20369,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20369,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20369,], +["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20369,], ["libraries.designsystem.atomic.pages_OnBoardingPage_Day_0_en","libraries.designsystem.atomic.pages_OnBoardingPage_Night_0_en",0,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20350,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20350,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20350,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20350,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20350,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20350,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20350,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20369,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20369,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20369,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20369,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20369,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20369,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20369,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20369,], ["libraries.designsystem.background_OnboardingBackground_Day_0_en","libraries.designsystem.background_OnboardingBackground_Night_0_en",0,], -["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20350,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20350,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20350,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20350,], +["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20369,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20369,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20369,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20369,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_12_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_12_en",0,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_13_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_13_en",0,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20350,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20350,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20350,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20350,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20350,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20350,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20350,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20350,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20350,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20369,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20369,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20369,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20369,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20369,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20369,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20369,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20369,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20369,], ["libraries.designsystem.theme.components_OutlinedButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonSmall_Buttons_en","",0,], -["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20350,], -["features.changeroommemberroles.impl_PendingMemberRowWithLongName_Day_0_en","features.changeroommemberroles.impl_PendingMemberRowWithLongName_Night_0_en",20350,], -["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20350,], -["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20350,], -["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20350,], -["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20350,], +["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20369,], +["features.changeroommemberroles.impl_PendingMemberRowWithLongName_Day_0_en","features.changeroommemberroles.impl_PendingMemberRowWithLongName_Night_0_en",20369,], +["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20369,], +["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20369,], +["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20369,], +["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20369,], ["features.lockscreen.impl.components_PinEntryTextField_Day_0_en","features.lockscreen.impl.components_PinEntryTextField_Night_0_en",0,], ["libraries.designsystem.components_PinIcon_Day_0_en","libraries.designsystem.components_PinIcon_Night_0_en",0,], ["features.lockscreen.impl.unlock.keypad_PinKeypad_Day_0_en","features.lockscreen.impl.unlock.keypad_PinKeypad_Night_0_en",0,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20350,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20350,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20350,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20350,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20350,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20350,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20350,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20350,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20350,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20350,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20350,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20350,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20350,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20350,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20350,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20350,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20369,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20369,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20369,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20369,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20369,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20369,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20369,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20369,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20369,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20369,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20369,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20369,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20369,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20369,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20369,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20369,], ["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_0_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_0_en",0,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",20350,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20350,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20350,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20350,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20350,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20350,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20350,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20350,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20350,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20350,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20350,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20350,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20350,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20350,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",20369,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20369,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20369,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20369,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20369,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20369,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20369,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20369,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20369,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20369,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20369,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20369,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20369,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20369,], ["libraries.designsystem.atomic.atoms_PlaceholderAtom_Day_0_en","libraries.designsystem.atomic.atoms_PlaceholderAtom_Night_0_en",0,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20350,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20350,], -["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20350,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20350,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20350,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20369,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20369,], +["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20369,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20369,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20369,], ["features.poll.api.pollcontent_PollAnswerViewUndisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewUndisclosedNotSelected_Night_0_en",0,], ["features.poll.api.pollcontent_PollAnswerViewUndisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewUndisclosedSelected_Night_0_en",0,], -["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",20350,], -["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20350,], -["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20350,], -["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20350,], -["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20350,], -["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20350,], -["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20350,], -["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20350,], -["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20350,], -["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20350,], -["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20350,], +["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",20369,], +["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20369,], +["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20369,], +["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20369,], +["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20369,], +["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20369,], +["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20369,], +["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20369,], +["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20369,], +["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20369,], +["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20369,], ["features.poll.api.pollcontent_PollTitleView_Day_0_en","features.poll.api.pollcontent_PollTitleView_Night_0_en",0,], ["libraries.designsystem.components.preferences_PreferenceCategory_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceCheckbox_Preferences_en","",0,], @@ -881,206 +914,208 @@ export const screenshots = [ ["libraries.designsystem.components.preferences_PreferenceRow_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSlide_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSwitch_Preferences_en","",0,], -["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20350,], -["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20350,], -["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20350,], -["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20350,], +["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20369,], +["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20369,], +["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20369,], +["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20369,], ["features.messages.impl.timeline.components.event_ProgressButton_Day_0_en","features.messages.impl.timeline.components.event_ProgressButton_Night_0_en",0,], -["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",20350,], -["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20350,], +["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",20369,], +["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20369,], ["libraries.designsystem.components_ProgressDialogWithTextAndContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithTextAndContent_Night_0_en",0,], -["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20350,], -["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20350,], -["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20350,], -["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20350,], -["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20350,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20350,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20350,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20350,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20350,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20350,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20350,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20350,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20350,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20350,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20350,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20350,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20350,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20350,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20350,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20350,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20350,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20350,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20350,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20350,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20350,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20350,], +["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20369,], +["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20369,], +["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20369,], +["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20369,], +["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20369,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20369,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20369,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20369,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20369,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20369,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20369,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20369,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20369,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20369,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20369,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20369,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20369,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20369,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20369,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20369,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20369,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20369,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20369,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20369,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20369,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20369,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20369,], ["libraries.designsystem.theme.components_RadioButton_Toggles_en","",0,], -["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20350,], -["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20350,], +["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20369,], +["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20369,], ["features.rageshake.api.preferences_RageshakePreferencesView_Day_1_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_1_en",0,], ["features.messages.impl.timeline.components.reactionsummary_ReactionSummaryViewContent_Day_0_en","features.messages.impl.timeline.components.reactionsummary_ReactionSummaryViewContent_Night_0_en",0,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20350,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20350,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20350,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20350,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20350,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20350,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20350,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20350,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20350,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20350,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20350,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20350,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20350,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20350,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20350,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20350,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20350,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20350,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20350,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20350,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20350,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20369,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20369,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20369,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20369,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20369,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20369,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20369,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20369,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20369,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20369,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20369,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20369,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20369,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20369,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20369,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20369,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20369,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20369,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20369,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20369,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20369,], ["libraries.designsystem.atomic.atoms_RedIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_RedIndicatorAtom_Night_0_en",0,], ["features.messages.impl.timeline.components_ReplySwipeIndicator_Day_0_en","features.messages.impl.timeline.components_ReplySwipeIndicator_Night_0_en",0,], -["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20350,], -["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20350,], -["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20350,], -["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20350,], -["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20350,], -["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20350,], -["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20350,], -["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20350,], -["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20350,], -["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20350,], -["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20350,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20350,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20350,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20350,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20350,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20350,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20350,], +["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20369,], +["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20369,], +["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20369,], +["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20369,], +["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20369,], +["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20369,], +["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20369,], +["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20369,], +["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20369,], +["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20369,], +["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20369,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20369,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20369,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20369,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20369,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20369,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20369,], ["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_0_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_0_en",0,], -["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",20350,], -["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20350,], -["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20350,], -["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20350,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en",20350,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en",20350,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en",20350,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en",20350,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en",20350,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en",20350,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en",20350,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en",20350,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_8_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_8_en",20350,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",20369,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20369,], +["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20369,], +["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20369,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en",20369,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en",20369,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en",20369,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en",20369,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en",20369,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en",20369,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en",20369,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en",20369,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_8_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_8_en",20369,], ["libraries.matrix.ui.room.address_RoomAddressField_Day_0_en","libraries.matrix.ui.room.address_RoomAddressField_Night_0_en",0,], ["features.roomaliasresolver.impl_RoomAliasResolverView_Day_0_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_0_en",0,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",20350,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20350,], -["features.roomdetails.impl_RoomDetailsDark_0_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_10_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_11_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_12_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_13_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_14_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_15_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_16_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_17_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_18_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_19_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_1_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_2_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_3_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_4_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_5_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_6_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_7_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_8_en","",20350,], -["features.roomdetails.impl_RoomDetailsDark_9_en","",20350,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_0_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_0_en",20350,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_1_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_1_en",20350,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_2_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_2_en",20350,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_3_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_3_en",20350,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_4_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_4_en",20350,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_5_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_5_en",20350,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_6_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_6_en",20350,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_7_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_7_en",20350,], -["features.roomdetails.impl_RoomDetails_0_en","",20350,], -["features.roomdetails.impl_RoomDetails_10_en","",20350,], -["features.roomdetails.impl_RoomDetails_11_en","",20350,], -["features.roomdetails.impl_RoomDetails_12_en","",20350,], -["features.roomdetails.impl_RoomDetails_13_en","",20350,], -["features.roomdetails.impl_RoomDetails_14_en","",20350,], -["features.roomdetails.impl_RoomDetails_15_en","",20350,], -["features.roomdetails.impl_RoomDetails_16_en","",20350,], -["features.roomdetails.impl_RoomDetails_17_en","",20350,], -["features.roomdetails.impl_RoomDetails_18_en","",20350,], -["features.roomdetails.impl_RoomDetails_19_en","",20350,], -["features.roomdetails.impl_RoomDetails_1_en","",20350,], -["features.roomdetails.impl_RoomDetails_2_en","",20350,], -["features.roomdetails.impl_RoomDetails_3_en","",20350,], -["features.roomdetails.impl_RoomDetails_4_en","",20350,], -["features.roomdetails.impl_RoomDetails_5_en","",20350,], -["features.roomdetails.impl_RoomDetails_6_en","",20350,], -["features.roomdetails.impl_RoomDetails_7_en","",20350,], -["features.roomdetails.impl_RoomDetails_8_en","",20350,], -["features.roomdetails.impl_RoomDetails_9_en","",20350,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20350,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20350,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20350,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20350,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20350,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20350,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20350,], -["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20350,], -["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20350,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",20369,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20369,], +["features.roomdetails.impl_RoomDetailsDark_0_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_10_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_11_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_12_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_13_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_14_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_15_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_16_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_17_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_18_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_19_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_1_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_2_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_3_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_4_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_5_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_6_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_7_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_8_en","",20369,], +["features.roomdetails.impl_RoomDetailsDark_9_en","",20369,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_0_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_0_en",20369,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_1_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_1_en",20369,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_2_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_2_en",20369,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_3_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_3_en",20369,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_4_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_4_en",20369,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_5_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_5_en",20369,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_6_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_6_en",20369,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_7_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_7_en",20369,], +["features.roomdetails.impl_RoomDetails_0_en","",20369,], +["features.roomdetails.impl_RoomDetails_10_en","",20369,], +["features.roomdetails.impl_RoomDetails_11_en","",20369,], +["features.roomdetails.impl_RoomDetails_12_en","",20369,], +["features.roomdetails.impl_RoomDetails_13_en","",20369,], +["features.roomdetails.impl_RoomDetails_14_en","",20369,], +["features.roomdetails.impl_RoomDetails_15_en","",20369,], +["features.roomdetails.impl_RoomDetails_16_en","",20369,], +["features.roomdetails.impl_RoomDetails_17_en","",20369,], +["features.roomdetails.impl_RoomDetails_18_en","",20369,], +["features.roomdetails.impl_RoomDetails_19_en","",20369,], +["features.roomdetails.impl_RoomDetails_1_en","",20369,], +["features.roomdetails.impl_RoomDetails_2_en","",20369,], +["features.roomdetails.impl_RoomDetails_3_en","",20369,], +["features.roomdetails.impl_RoomDetails_4_en","",20369,], +["features.roomdetails.impl_RoomDetails_5_en","",20369,], +["features.roomdetails.impl_RoomDetails_6_en","",20369,], +["features.roomdetails.impl_RoomDetails_7_en","",20369,], +["features.roomdetails.impl_RoomDetails_8_en","",20369,], +["features.roomdetails.impl_RoomDetails_9_en","",20369,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20369,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20369,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20369,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20369,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20369,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20369,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20369,], +["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20369,], +["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20369,], ["features.home.impl.components_RoomListContentView_Day_2_en","features.home.impl.components_RoomListContentView_Night_2_en",0,], -["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20350,], -["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20350,], -["features.home.impl.roomlist_RoomListDeclineInviteMenuContent_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenuContent_Night_0_en",20350,], -["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20350,], -["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20350,], -["features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_0_en","features.home.impl.roomlist_RoomListModalBottomSheetContent_Night_0_en",20350,], -["features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_1_en","features.home.impl.roomlist_RoomListModalBottomSheetContent_Night_1_en",20350,], -["features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_2_en","features.home.impl.roomlist_RoomListModalBottomSheetContent_Night_2_en",20350,], +["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20369,], +["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20369,], +["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20369,], +["features.home.impl.roomlist_RoomListDeclineInviteMenuContent_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenuContent_Night_0_en",20369,], +["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20369,], +["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20369,], +["features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_0_en","features.home.impl.roomlist_RoomListModalBottomSheetContent_Night_0_en",20369,], +["features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_1_en","features.home.impl.roomlist_RoomListModalBottomSheetContent_Night_1_en",20369,], +["features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_2_en","features.home.impl.roomlist_RoomListModalBottomSheetContent_Night_2_en",20369,], ["features.home.impl.search_RoomListSearchContent_Day_0_en","features.home.impl.search_RoomListSearchContent_Night_0_en",0,], -["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20350,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_0_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_0_en",20350,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_1_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_1_en",20350,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_2_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_2_en",20350,], -["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20350,], -["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20350,], -["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20350,], -["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20350,], -["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20350,], -["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20350,], +["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20369,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_0_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_0_en",20369,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_1_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_1_en",20369,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_2_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_2_en",20369,], +["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20369,], +["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20369,], +["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20369,], +["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20369,], +["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20369,], +["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20369,], ["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",0,], -["features.roomdetails.impl.members_RoomMemberListView_Day_7_en","features.roomdetails.impl.members_RoomMemberListView_Night_7_en",20350,], -["features.roomdetails.impl.members_RoomMemberListView_Day_8_en","features.roomdetails.impl.members_RoomMemberListView_Night_8_en",20350,], -["features.roomdetails.impl.members_RoomMemberListView_Day_9_en","features.roomdetails.impl.members_RoomMemberListView_Night_9_en",20350,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20350,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20350,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20350,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20350,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20350,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20350,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20350,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20350,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20350,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20350,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20350,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20350,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20350,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20350,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20350,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20350,], -["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20350,], -["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20350,], -["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20350,], -["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20350,], -["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20350,], -["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20350,], +["features.roomdetails.impl.members_RoomMemberListView_Day_7_en","features.roomdetails.impl.members_RoomMemberListView_Night_7_en",20369,], +["features.roomdetails.impl.members_RoomMemberListView_Day_8_en","features.roomdetails.impl.members_RoomMemberListView_Night_8_en",20369,], +["features.roomdetails.impl.members_RoomMemberListView_Day_9_en","features.roomdetails.impl.members_RoomMemberListView_Night_9_en",20369,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20369,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20369,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20369,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20369,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20369,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20369,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20369,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20369,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20369,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20369,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20369,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20369,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20369,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20369,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20369,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20369,], +["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20369,], +["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20369,], +["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20369,], +["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20369,], +["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20369,], +["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20369,], ["features.home.impl.components_RoomSummaryPlaceholderRow_Day_0_en","features.home.impl.components_RoomSummaryPlaceholderRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_0_en","features.home.impl.components_RoomSummaryRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_10_en","features.home.impl.components_RoomSummaryRow_Night_10_en",0,], @@ -1103,13 +1138,14 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_26_en","features.home.impl.components_RoomSummaryRow_Night_26_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_27_en","features.home.impl.components_RoomSummaryRow_Night_27_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_28_en","features.home.impl.components_RoomSummaryRow_Night_28_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20350,], -["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20350,], -["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20350,], -["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20350,], -["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20350,], -["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20350,], -["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20350,], +["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20369,], +["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20369,], +["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20369,], +["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20369,], +["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20369,], +["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20369,], +["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20369,], +["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20369,], ["features.home.impl.components_RoomSummaryRow_Day_3_en","features.home.impl.components_RoomSummaryRow_Night_3_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_4_en","features.home.impl.components_RoomSummaryRow_Night_4_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_5_en","features.home.impl.components_RoomSummaryRow_Night_5_en",0,], @@ -1117,80 +1153,80 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_7_en","features.home.impl.components_RoomSummaryRow_Night_7_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_8_en","features.home.impl.components_RoomSummaryRow_Night_8_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_9_en","features.home.impl.components_RoomSummaryRow_Night_9_en",0,], -["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20350,], -["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20350,], -["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20350,], +["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20369,], +["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20369,], +["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20369,], ["appicon.enterprise_RoundIcon_en","",0,], ["appicon.element_RoundIcon_en","",0,], ["libraries.designsystem.atomic.atoms_RoundedIconAtom_Day_0_en","libraries.designsystem.atomic.atoms_RoundedIconAtom_Night_0_en",0,], -["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20350,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20350,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20350,], +["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20369,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20369,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20369,], ["libraries.designsystem.theme.components_SearchBarActiveNoneQuery_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchBarActiveWithContent_Search_views_en","",0,], -["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en","",20350,], +["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en","",20369,], ["libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchBarInactive_Search_views_en","",0,], -["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20350,], -["features.startchat.impl.components_SearchSingleUserResultItem_en","",20350,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20350,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20350,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20350,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20350,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20350,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20350,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20350,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20350,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20350,], -["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20350,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20350,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20350,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20350,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20350,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20350,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20350,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20350,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20350,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20350,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20350,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20350,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_0_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_1_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_2_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_3_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_4_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_5_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_6_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_7_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_8_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_0_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_1_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_2_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_3_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_4_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_5_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_6_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_7_en","",20350,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_8_en","",20350,], +["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20369,], +["features.startchat.impl.components_SearchSingleUserResultItem_en","",20369,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20369,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20369,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20369,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20369,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20369,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20369,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20369,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20369,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20369,], +["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20369,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20369,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20369,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20369,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20369,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20369,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20369,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20369,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20369,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20369,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20369,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20369,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_0_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_1_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_2_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_3_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_4_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_5_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_6_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_7_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_8_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_0_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_1_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_2_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_3_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_4_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_5_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_6_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_7_en","",20369,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_8_en","",20369,], ["libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_0_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_1_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_1_en",0,], @@ -1204,11 +1240,11 @@ export const screenshots = [ ["libraries.matrix.ui.components_SelectedUser_Day_1_en","libraries.matrix.ui.components_SelectedUser_Night_1_en",0,], ["libraries.matrix.ui.components_SelectedUsersRowList_Day_0_en","libraries.matrix.ui.components_SelectedUsersRowList_Night_0_en",0,], ["libraries.textcomposer.components_SendButton_Day_0_en","libraries.textcomposer.components_SendButton_Night_0_en",0,], -["features.location.impl.send_SendLocationView_Day_0_en","features.location.impl.send_SendLocationView_Night_0_en",20350,], -["features.location.impl.send_SendLocationView_Day_1_en","features.location.impl.send_SendLocationView_Night_1_en",20350,], -["features.location.impl.send_SendLocationView_Day_2_en","features.location.impl.send_SendLocationView_Night_2_en",20350,], -["features.location.impl.send_SendLocationView_Day_3_en","features.location.impl.send_SendLocationView_Night_3_en",20350,], -["features.location.impl.send_SendLocationView_Day_4_en","features.location.impl.send_SendLocationView_Night_4_en",20350,], +["features.location.impl.send_SendLocationView_Day_0_en","features.location.impl.send_SendLocationView_Night_0_en",20369,], +["features.location.impl.send_SendLocationView_Day_1_en","features.location.impl.send_SendLocationView_Night_1_en",20369,], +["features.location.impl.send_SendLocationView_Day_2_en","features.location.impl.send_SendLocationView_Night_2_en",20369,], +["features.location.impl.send_SendLocationView_Day_3_en","features.location.impl.send_SendLocationView_Night_3_en",20369,], +["features.location.impl.send_SendLocationView_Day_4_en","features.location.impl.send_SendLocationView_Night_4_en",20369,], ["libraries.matrix.ui.messages.sender_SenderName_Day_0_en","libraries.matrix.ui.messages.sender_SenderName_Night_0_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_1_en","libraries.matrix.ui.messages.sender_SenderName_Night_1_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_2_en","libraries.matrix.ui.messages.sender_SenderName_Night_2_en",0,], @@ -1218,27 +1254,27 @@ export const screenshots = [ ["libraries.matrix.ui.messages.sender_SenderName_Day_6_en","libraries.matrix.ui.messages.sender_SenderName_Night_6_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_7_en","libraries.matrix.ui.messages.sender_SenderName_Night_7_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_8_en","libraries.matrix.ui.messages.sender_SenderName_Night_8_en",0,], -["features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20350,], -["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20350,], -["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20350,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20350,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20350,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20350,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20350,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20350,], +["features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20369,], +["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20369,], +["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20369,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20369,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20369,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20369,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20369,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20369,], ["features.share.impl_ShareView_Day_0_en","features.share.impl_ShareView_Night_0_en",0,], ["features.share.impl_ShareView_Day_1_en","features.share.impl_ShareView_Night_1_en",0,], ["features.share.impl_ShareView_Day_2_en","features.share.impl_ShareView_Night_2_en",0,], -["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",20350,], -["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20350,], -["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20350,], -["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20350,], -["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20350,], -["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20350,], -["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20350,], -["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20350,], -["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20350,], -["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20350,], +["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",20369,], +["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20369,], +["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20369,], +["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20369,], +["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20369,], +["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20369,], +["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20369,], +["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20369,], +["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20369,], +["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20369,], ["libraries.designsystem.components.dialogs_SingleSelectionDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_SingleSelectionDialog_Day_0_en","libraries.designsystem.components.dialogs_SingleSelectionDialog_Night_0_en",0,], ["libraries.designsystem.components.list_SingleSelectionListItemCustomFormattert_Single_selection_List_item_-_custom_formatter_List_items_en","",0,], @@ -1247,86 +1283,97 @@ export const screenshots = [ ["libraries.designsystem.components.list_SingleSelectionListItemUnselectedWithSupportingText_Single_selection_List_item_-_no_selection,_supporting_text_List_items_en","",0,], ["libraries.designsystem.components.list_SingleSelectionListItem_Single_selection_List_item_-_no_selection_List_items_en","",0,], ["libraries.designsystem.theme.components_Sliders_Sliders_en","",0,], -["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",20350,], +["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",20369,], ["libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar_with_action_and_close_button_Snackbars_en","",0,], ["libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar_with_action_and_close_button_on_new_line_Snackbars_en","",0,], ["libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar_with_action_on_new_line_Snackbars_en","",0,], ["libraries.designsystem.theme.components_SnackbarWithAction_Snackbar_with_action_Snackbars_en","",0,], ["libraries.designsystem.theme.components_Snackbar_Snackbar_Snackbars_en","",0,], +["features.announcement.impl.spaces_SpaceAnnouncementView_Day_0_en","features.announcement.impl.spaces_SpaceAnnouncementView_Night_0_en",20369,], ["libraries.designsystem.components.avatar.internal_SpaceAvatar_Avatars_en","",0,], -["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20350,], -["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20350,], -["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20350,], +["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20369,], +["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20369,], +["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20369,], ["libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Day_0_en","libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Night_0_en",0,], ["libraries.matrix.ui.components_SpaceMembersView_Day_0_en","libraries.matrix.ui.components_SpaceMembersView_Night_0_en",0,], -["features.space.impl_SpaceView_Day_0_en","features.space.impl_SpaceView_Night_0_en",0,], -["features.space.impl_SpaceView_Day_1_en","features.space.impl_SpaceView_Night_1_en",0,], -["features.space.impl_SpaceView_Day_2_en","features.space.impl_SpaceView_Night_2_en",20350,], -["features.space.impl_SpaceView_Day_3_en","features.space.impl_SpaceView_Night_3_en",20350,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20369,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20369,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20369,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20369,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20369,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20369,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20369,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20369,], +["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20369,], +["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20369,], +["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20369,], +["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20369,], +["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20369,], +["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20369,], ["libraries.designsystem.modifiers_SquareSizeModifierInsideSquare_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeHeight_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeWidth_en","",0,], -["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20350,], -["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20350,], -["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20350,], -["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20350,], -["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20350,], -["features.startchat.impl.root_StartChatView_Day_5_en","features.startchat.impl.root_StartChatView_Night_5_en",20350,], -["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20350,], +["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20369,], +["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20369,], +["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20369,], +["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20369,], +["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20369,], +["features.startchat.impl.root_StartChatView_Day_5_en","features.startchat.impl.root_StartChatView_Night_5_en",20369,], +["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20369,], ["features.location.api_StaticMapView_Day_0_en","features.location.api_StaticMapView_Night_0_en",0,], -["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",20350,], +["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",20369,], ["libraries.designsystem.atomic.pages_SunsetPage_Day_0_en","libraries.designsystem.atomic.pages_SunsetPage_Night_0_en",0,], ["libraries.designsystem.components.button_SuperButton_Day_0_en","libraries.designsystem.components.button_SuperButton_Night_0_en",0,], ["libraries.designsystem.theme.components_Surface_en","",0,], ["libraries.designsystem.theme.components_Switch_Toggles_en","",0,], -["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",20350,], +["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",20369,], ["libraries.designsystem.components.avatar.internal_TextAvatar_Avatars_en","",0,], ["libraries.designsystem.theme.components_TextButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonSmall_Buttons_en","",0,], -["libraries.textcomposer_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20350,], -["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20350,], -["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20350,], -["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20350,], -["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20350,], -["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20350,], -["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20350,], -["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20350,], -["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20350,], -["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20350,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20350,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20350,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20350,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20350,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20350,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20350,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20350,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20350,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20350,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20350,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20350,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20350,], -["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20350,], -["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20350,], -["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20350,], -["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20350,], -["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20350,], -["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20350,], -["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20350,], -["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20350,], -["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20350,], -["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20350,], -["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20350,], -["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20350,], -["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20350,], -["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20350,], -["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20350,], +["libraries.textcomposer_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20369,], +["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20369,], +["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20369,], +["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20369,], +["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20369,], +["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20369,], +["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20369,], +["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20369,], +["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20369,], +["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20369,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20369,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20369,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20369,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20369,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20369,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20369,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20369,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20369,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20369,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20369,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20369,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20369,], +["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20369,], +["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20369,], +["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20369,], +["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20369,], +["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20369,], +["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20369,], +["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20369,], +["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20369,], +["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20369,], +["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20369,], +["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20369,], +["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20369,], +["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20369,], +["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20369,], +["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20369,], ["libraries.textcomposer_TextComposerVoice_Day_0_en","libraries.textcomposer_TextComposerVoice_Night_0_en",0,], ["libraries.designsystem.theme.components_TextDark_Text_en","",0,], -["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20350,], -["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20350,], +["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20369,], +["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20369,], ["libraries.designsystem.components.list_TextFieldListItemEmpty_Text_field_List_item_-_empty_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text_field_List_item_-_textfieldvalue_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItem_Text_field_List_item_-_text_List_items_en","",0,], @@ -1338,14 +1385,16 @@ export const screenshots = [ ["libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_3_en","libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_3_en",0,], ["libraries.textcomposer.components_TextFormatting_Day_0_en","libraries.textcomposer.components_TextFormatting_Night_0_en",0,], ["libraries.designsystem.theme.components_TextLight_Text_en","",0,], -["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20350,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20350,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20350,], +["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20369,], +["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20369,], +["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20369,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20369,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20369,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_0_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_1_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_2_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",20350,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20350,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",20369,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20369,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_5_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_6_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_6_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_7_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_7_en",0,], @@ -1355,18 +1404,18 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_2_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_4_en",0,], -["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20350,], +["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20369,], ["features.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_Night_0_en",0,], ["features.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_Day_1_en","features.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_Night_1_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",20350,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20350,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20350,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20350,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20350,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20350,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20350,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20350,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20350,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",20369,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20369,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20369,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20369,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20369,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20369,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20369,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20369,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20369,], ["features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowLongSenderName_en","",0,], @@ -1374,18 +1423,18 @@ export const screenshots = [ ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20350,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20350,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20369,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20369,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_5_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_6_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_6_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20350,], -["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20350,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20350,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20369,], +["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20369,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20369,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",20350,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20350,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",20369,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20369,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en",0,], @@ -1394,41 +1443,41 @@ export const screenshots = [ ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20350,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20369,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_6_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_6_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20350,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20369,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20350,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20369,], ["features.messages.impl.timeline.components_TimelineItemEventRow_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRow_Night_0_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",20350,], +["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",20369,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_2_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_4_en",0,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20350,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20350,], -["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20350,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20369,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20369,], +["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20369,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_2_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemInformativeView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemInformativeView_Night_0_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20350,], +["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20369,], ["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20350,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20350,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20350,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20350,], -["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20350,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20369,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20369,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20369,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20369,], +["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20369,], ["features.messages.impl.timeline.components_TimelineItemReactionsViewFew_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewFew_Night_0_en",0,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",20350,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20350,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",20369,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20369,], ["features.messages.impl.timeline.components_TimelineItemReactionsView_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsView_Night_0_en",0,], -["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",20350,], +["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",20369,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_0_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_0_en",0,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_1_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_1_en",0,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_2_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_2_en",0,], @@ -1437,8 +1486,8 @@ export const screenshots = [ ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_5_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_5_en",0,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_6_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_6_en",0,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_7_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_7_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20350,], -["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20350,], +["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20369,], +["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20369,], ["features.messages.impl.timeline.components_TimelineItemStateEventRow_Day_0_en","features.messages.impl.timeline.components_TimelineItemStateEventRow_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemStateView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemStateView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemStickerView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemStickerView_Night_0_en",0,], @@ -1453,8 +1502,8 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_4_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_5_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20350,], -["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20350,], +["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20369,], +["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20369,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_2_en",0,], @@ -1477,85 +1526,85 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemVoiceView_Day_9_en","features.messages.impl.timeline.components.event_TimelineItemVoiceView_Night_9_en",0,], ["features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Day_0_en","features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Night_0_en",0,], -["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20350,], -["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20350,], +["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20369,], +["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20369,], ["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",0,], -["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20350,], -["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20350,], -["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20350,], -["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20350,], -["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20350,], -["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20350,], -["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20350,], -["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20350,], +["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20369,], +["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20369,], +["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20369,], +["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20369,], +["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20369,], +["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20369,], +["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20369,], +["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20369,], ["features.messages.impl.timeline_TimelineView_Day_2_en","features.messages.impl.timeline_TimelineView_Night_2_en",0,], ["features.messages.impl.timeline_TimelineView_Day_3_en","features.messages.impl.timeline_TimelineView_Night_3_en",0,], -["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",20350,], +["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",20369,], ["features.messages.impl.timeline_TimelineView_Day_5_en","features.messages.impl.timeline_TimelineView_Night_5_en",0,], -["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",20350,], +["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",20369,], ["features.messages.impl.timeline_TimelineView_Day_7_en","features.messages.impl.timeline_TimelineView_Night_7_en",0,], -["features.messages.impl.timeline_TimelineView_Day_8_en","features.messages.impl.timeline_TimelineView_Night_8_en",20350,], +["features.messages.impl.timeline_TimelineView_Day_8_en","features.messages.impl.timeline_TimelineView_Night_8_en",20369,], ["features.messages.impl.timeline_TimelineView_Day_9_en","features.messages.impl.timeline_TimelineView_Night_9_en",0,], ["libraries.designsystem.components.avatar.internal_TombstonedRoomAvatar_Avatars_en","",0,], ["libraries.designsystem.theme.components_TopAppBarStr_App_Bars_en","",0,], ["libraries.designsystem.theme.components_TopAppBar_App_Bars_en","",0,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20350,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20350,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20350,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20350,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20350,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20350,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20350,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20350,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20369,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20369,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20369,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20369,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20369,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20369,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20369,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20369,], ["features.messages.impl.typing_TypingNotificationView_Day_0_en","features.messages.impl.typing_TypingNotificationView_Night_0_en",0,], -["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",20350,], -["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20350,], -["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20350,], -["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20350,], -["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20350,], -["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20350,], +["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",20369,], +["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20369,], +["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20369,], +["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20369,], +["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20369,], +["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20369,], ["features.messages.impl.typing_TypingNotificationView_Day_7_en","features.messages.impl.typing_TypingNotificationView_Night_7_en",0,], ["features.messages.impl.typing_TypingNotificationView_Day_8_en","features.messages.impl.typing_TypingNotificationView_Night_8_en",0,], ["libraries.designsystem.atomic.atoms_UnreadIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_UnreadIndicatorAtom_Night_0_en",0,], -["libraries.matrix.ui.components_UnresolvedUserRow_en","",20350,], +["libraries.matrix.ui.components_UnresolvedUserRow_en","",20369,], ["libraries.matrix.ui.components_UnsavedAvatar_Day_0_en","libraries.matrix.ui.components_UnsavedAvatar_Night_0_en",0,], ["libraries.designsystem.components.avatar.internal_UserAvatarColors_Day_0_en","libraries.designsystem.components.avatar.internal_UserAvatarColors_Night_0_en",0,], -["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20350,], -["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20350,], -["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20350,], -["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20350,], +["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20369,], +["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20369,], +["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20369,], +["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20369,], ["features.startchat.impl.components_UserListView_Day_3_en","features.startchat.impl.components_UserListView_Night_3_en",0,], ["features.startchat.impl.components_UserListView_Day_4_en","features.startchat.impl.components_UserListView_Night_4_en",0,], ["features.startchat.impl.components_UserListView_Day_5_en","features.startchat.impl.components_UserListView_Night_5_en",0,], ["features.startchat.impl.components_UserListView_Day_6_en","features.startchat.impl.components_UserListView_Night_6_en",0,], -["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20350,], +["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20369,], ["features.startchat.impl.components_UserListView_Day_8_en","features.startchat.impl.components_UserListView_Night_8_en",0,], -["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20350,], +["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20369,], ["features.preferences.impl.user_UserPreferences_Day_0_en","features.preferences.impl.user_UserPreferences_Night_0_en",0,], ["features.preferences.impl.user_UserPreferences_Day_1_en","features.preferences.impl.user_UserPreferences_Night_1_en",0,], ["features.preferences.impl.user_UserPreferences_Day_2_en","features.preferences.impl.user_UserPreferences_Night_2_en",0,], -["features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20350,], -["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20350,], -["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20350,], -["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20350,], -["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20350,], -["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20350,], -["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20350,], -["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20350,], -["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20350,], -["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20350,], -["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20350,], -["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20350,], +["features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20369,], +["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20369,], +["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20369,], +["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20369,], +["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20369,], +["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20369,], +["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20369,], +["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20369,], +["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20369,], +["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20369,], +["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20369,], +["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20369,], ["features.verifysession.impl.ui_VerificationUserProfileContent_Day_0_en","features.verifysession.impl.ui_VerificationUserProfileContent_Night_0_en",0,], ["libraries.designsystem.ruler_VerticalRuler_Day_0_en","libraries.designsystem.ruler_VerticalRuler_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_1_en",0,], -["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20350,], -["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20350,], +["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20369,], +["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20369,], ["features.viewfolder.impl.file_ViewFileView_Day_0_en","features.viewfolder.impl.file_ViewFileView_Night_0_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_1_en","features.viewfolder.impl.file_ViewFileView_Night_1_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_2_en","features.viewfolder.impl.file_ViewFileView_Night_2_en",0,], -["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20350,], +["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20369,], ["features.viewfolder.impl.file_ViewFileView_Day_4_en","features.viewfolder.impl.file_ViewFileView_Night_4_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_5_en","features.viewfolder.impl.file_ViewFileView_Night_5_en",0,], ["features.viewfolder.impl.folder_ViewFolderView_Day_0_en","features.viewfolder.impl.folder_ViewFolderView_Night_0_en",0,], diff --git a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt index d80a8288ad..04f0f6867a 100644 --- a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt +++ b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt @@ -17,6 +17,7 @@ import im.vector.app.features.analytics.itf.VectorAnalyticsScreen import im.vector.app.features.analytics.plan.SuperProperties import im.vector.app.features.analytics.plan.UserProperties import io.element.android.libraries.di.annotations.AppCoroutineScope +import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.api.observer.SessionListener import io.element.android.libraries.sessionstorage.api.observer.SessionObserver import io.element.android.services.analytics.api.AnalyticsService @@ -40,6 +41,7 @@ class DefaultAnalyticsService( @AppCoroutineScope private val coroutineScope: CoroutineScope, private val sessionObserver: SessionObserver, + private val sessionStore: SessionStore, ) : AnalyticsService, SessionListener { // Cache for the store values private val userConsent = AtomicBoolean(false) @@ -80,8 +82,10 @@ class DefaultAnalyticsService( } override suspend fun onSessionDeleted(userId: String) { - // Delete the store - analyticsStore.reset() + // Delete the store when the last session is deleted + if (sessionStore.getAllSessions().isEmpty()) { + analyticsStore.reset() + } } private fun observeUserConsent() { diff --git a/services/analytics/impl/src/test/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsServiceTest.kt b/services/analytics/impl/src/test/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsServiceTest.kt index 6e65303761..1e5a54fb26 100644 --- a/services/analytics/impl/src/test/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsServiceTest.kt +++ b/services/analytics/impl/src/test/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsServiceTest.kt @@ -16,7 +16,9 @@ import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.PollEnd import im.vector.app.features.analytics.plan.SuperProperties import im.vector.app.features.analytics.plan.UserProperties +import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.api.observer.SessionObserver +import io.element.android.libraries.sessionstorage.test.InMemorySessionStore import io.element.android.libraries.sessionstorage.test.observer.NoOpSessionObserver import io.element.android.services.analytics.impl.store.AnalyticsStore import io.element.android.services.analytics.impl.store.FakeAnalyticsStore @@ -167,7 +169,7 @@ class DefaultAnalyticsServiceTest { } @Test - fun `when a session is deleted, the store is reset`() = runTest { + fun `when the last session is deleted, the store is reset`() = runTest { val resetLambda = lambdaRecorder { } val store = FakeAnalyticsStore( resetLambda = resetLambda, @@ -258,11 +260,13 @@ class DefaultAnalyticsServiceTest { ), analyticsStore: AnalyticsStore = FakeAnalyticsStore(), sessionObserver: SessionObserver = NoOpSessionObserver(), + sessionStore: SessionStore = InMemorySessionStore(), ) = DefaultAnalyticsService( analyticsProviders = analyticsProviders, analyticsStore = analyticsStore, coroutineScope = coroutineScope, sessionObserver = sessionObserver, + sessionStore = sessionStore, ).also { // Wait for the service to be ready delay(1) 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 8a0e6d0966..234a9c30ff 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 @@ -62,6 +62,7 @@ class KonsistClassNameTest { .withAllParentsOf(PreviewParameterProvider::class) .withoutName( "AspectRatioProvider", + "LoginModeViewErrorProvider", "OverlapRatioProvider", "TextFileContentProvider", ) diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistComposableTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistComposableTest.kt index d1435e409d..181c63350b 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistComposableTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistComposableTest.kt @@ -36,6 +36,15 @@ class KonsistComposableTest { "OutlinedButton", "SimpleAlertDialogContent", "TextButton", + "AvatarColorsPreviewLight", + "AvatarColorsPreviewDark", + "IconsCompoundPreviewLight", + "IconsCompoundPreviewRtl", + "IconsCompoundPreviewDark", + "CompoundSemanticColorsLight", + "CompoundSemanticColorsLightHc", + "CompoundSemanticColorsDark", + "CompoundSemanticColorsDarkHc", ) .assertTrue( additionalMessage = diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistDiTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistDiTest.kt new file mode 100644 index 0000000000..093cfd4488 --- /dev/null +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistDiTest.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2025 New Vector 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.konsist + +import com.lemonappdev.konsist.api.Konsist +import com.lemonappdev.konsist.api.ext.list.withAnnotationOf +import com.lemonappdev.konsist.api.ext.list.withParameter +import com.lemonappdev.konsist.api.verify.assertTrue +import dev.zacsweers.metro.Assisted +import dev.zacsweers.metro.Inject +import org.junit.Test + +class KonsistDiTest { + @Test + fun `class annotated with @Inject should not have constructors with @Assisted parameter`() { + Konsist + .scopeFromProject() + .classes() + .withAnnotationOf(Inject::class) + .assertTrue( + additionalMessage = "Class with @Assisted parameter in constructor should be annotated with @AssistedInject and not @Inject" + ) { classDeclaration -> + classDeclaration.constructors + .withParameter { parameterDeclaration -> + parameterDeclaration.hasAnnotationOf(Assisted::class) + } + .isEmpty() + } + } +} 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 new file mode 100644 index 0000000000..0aa68503e5 --- /dev/null +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistImmutableTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2023, 2024 New Vector 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.konsist + +import com.lemonappdev.konsist.api.Konsist +import com.lemonappdev.konsist.api.ext.list.withoutName +import com.lemonappdev.konsist.api.verify.assertFalse +import org.junit.Test + +class KonsistImmutableTest { + /** + * toPersistentList() returns a PersistentList which allow mutations, while toImmutableList() returns + * an ImmutableList which does not allow mutations. Generally, we do not use the mutation features, + * so we should prefer toImmutableList. + */ + @Test + fun `toPersistentList() should not be used instead of toImmutableList()`() { + Konsist + .scopeFromProject() + .functions() + .withoutName("toPersistentList() should not be used instead of toImmutableList()") + .assertFalse(additionalMessage = "Please use toImmutableList() instead of toPersistentList()") { + it.text.contains(".toPersistentList()") + } + } + + /** + * toPersistentSet() returns a PersistentSet which allow mutations, while toImmutableSet() returns + * an ImmutableSet which does not allow mutations. Generally, we do not use the mutation features, + * so we should prefer toImmutableSet. + */ + @Test + fun `toPersistentSet() should not be used instead of toImmutableSet()`() { + Konsist + .scopeFromProject() + .functions() + .withoutName("toPersistentSet() should not be used instead of toImmutableSet()") + .assertFalse(additionalMessage = "Please use toImmutableSet() instead of toPersistentSet()") { + it.text.contains(".toPersistentSet()") + } + } + + /** + * toPersistentMap() returns a PersistentMap which allow mutations, while toImmutableMap() returns + * an ImmutableMap which does not allow mutations. Generally, we do not use the mutation features, + * so we should prefer toImmutableMap. + */ + @Test + fun `toPersistentMap() should not be used instead of toImmutableMap()`() { + Konsist + .scopeFromProject() + .functions() + .withoutName("toPersistentMap() should not be used instead of toImmutableMap()") + .assertFalse(additionalMessage = "Please use toImmutableMap() instead of toPersistentMap()") { + it.text.contains(".toPersistentMap()") + } + } +} 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 9cbd2efdc4..082a106be2 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 @@ -82,6 +82,7 @@ class KonsistPreviewTest { "BackgroundVerticalGradientEnterprisePreview", "BackgroundVerticalGradientPreview", "ColorAliasesPreview", + "DefaultRoomListTopBarMultiAccountPreview", "DefaultRoomListTopBarWithIndicatorPreview", "FocusedEventEnterprisePreview", "FocusedEventPreview", diff --git a/tests/uitests/src/test/snapshots/images/features.announcement.impl.spaces_SpaceAnnouncementView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.announcement.impl.spaces_SpaceAnnouncementView_Day_0_en.png new file mode 100644 index 0000000000..e74a44bc85 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.announcement.impl.spaces_SpaceAnnouncementView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:780559f9a0677b007c93f0c24bef94ab63c89f2d8c124084557c5c8dec0ea3d6 +size 60500 diff --git a/tests/uitests/src/test/snapshots/images/features.announcement.impl.spaces_SpaceAnnouncementView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.announcement.impl.spaces_SpaceAnnouncementView_Night_0_en.png new file mode 100644 index 0000000000..3634085549 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.announcement.impl.spaces_SpaceAnnouncementView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:97e068729df68971b35727236873cc7954ae5e63890f255948fb4ccd98b3ae2f +size 59572 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_DefaultRoomListTopBarMultiAccount_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_DefaultRoomListTopBarMultiAccount_Day_0_en.png new file mode 100644 index 0000000000..dd337cac3b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_DefaultRoomListTopBarMultiAccount_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b289624c8461e08c945a254eb629ea536552e2652c7182be21a7bd9f183da022 +size 26185 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_DefaultRoomListTopBarMultiAccount_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_DefaultRoomListTopBarMultiAccount_Night_0_en.png new file mode 100644 index 0000000000..640aebb723 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_DefaultRoomListTopBarMultiAccount_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b86aaf39367bd454a913c4ed6cd17bbaff52e54349e0e6d363d6f84ccdc43c5f +size 23678 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_NewNotificationSoundBanner_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_NewNotificationSoundBanner_Day_0_en.png new file mode 100644 index 0000000000..dbfe7bfa4a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_NewNotificationSoundBanner_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:367bec41d3cf5e7d0e369a4610584c5a11ca7a69fe26b794290c49027de465a4 +size 23221 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_NewNotificationSoundBanner_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_NewNotificationSoundBanner_Night_0_en.png new file mode 100644 index 0000000000..0d2f51d4c1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_NewNotificationSoundBanner_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e666d80cb22b979a075ee9b749a36c4f9a4874149ba4d736d745c8311f6e0e85 +size 22563 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Day_5_en.png new file mode 100644 index 0000000000..e1efabe407 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Day_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf85a6a94ee3eed576e91c7a3a55e45571dff4a6dce1f6c6bbd22845fb01a76b +size 59896 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Night_5_en.png new file mode 100644 index 0000000000..70f3c084fe --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Night_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4646f1622c26a45208fa8ad59c9f531c1794e1874e31e50942f459bf3be3de2 +size 58927 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Day_32_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Day_32_en.png index 172695b6b2..89cf315b42 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Day_32_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Day_32_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ad2d62e2ce270b8548ddf611c3b192e756be0896627ccad28fff84c4ce5e2a1 -size 11938 +oid sha256:98d4e9152dee861cc7afdb3efe57b086c3d9d58d38eeb1345749d31debbf6017 +size 21266 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Day_33_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Day_33_en.png index 5430afc3cc..172695b6b2 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Day_33_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Day_33_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:541b4210ff0acaf6451a0ce9c6cdc4f120c460086e036fc27e319daa23c7082b -size 17393 +oid sha256:1ad2d62e2ce270b8548ddf611c3b192e756be0896627ccad28fff84c4ce5e2a1 +size 11938 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Day_34_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Day_34_en.png index ddd04d3eda..5430afc3cc 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Day_34_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Day_34_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:697513adb4fa43d68e8a4477f248e68f64ba305f0ecb6a2e681b8a8b04ceee2b -size 14823 +oid sha256:541b4210ff0acaf6451a0ce9c6cdc4f120c460086e036fc27e319daa23c7082b +size 17393 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Day_35_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Day_35_en.png new file mode 100644 index 0000000000..ddd04d3eda --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Day_35_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:697513adb4fa43d68e8a4477f248e68f64ba305f0ecb6a2e681b8a8b04ceee2b +size 14823 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Night_32_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Night_32_en.png index b24babae66..c7a3fd69f5 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Night_32_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Night_32_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0c73cab66c885e528bd8723b14cfe28356f0843fd360164c2d6163833a9576b -size 11999 +oid sha256:c24a3b08afe224e9919dd3818c72bd2ff8dd3f38663a6465a10b301ad91e8787 +size 20377 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Night_33_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Night_33_en.png index 50455e8053..b24babae66 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Night_33_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Night_33_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:71e4c4f995456a5502fb057bbb37cebcb9b92af6edf315cff63f92b3541a30a2 -size 17234 +oid sha256:f0c73cab66c885e528bd8723b14cfe28356f0843fd360164c2d6163833a9576b +size 11999 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Night_34_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Night_34_en.png index 0ef7d945d7..50455e8053 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Night_34_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Night_34_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:466ff5436718dd1871635330108c592ff28727c1628368c11387a9b1f5c73642 -size 14204 +oid sha256:71e4c4f995456a5502fb057bbb37cebcb9b92af6edf315cff63f92b3541a30a2 +size 17234 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Night_35_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Night_35_en.png new file mode 100644 index 0000000000..0ef7d945d7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomSummaryRow_Night_35_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:466ff5436718dd1871635330108c592ff28727c1628368c11387a9b1f5c73642 +size 14204 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 df4bd9bbc2..4b264c39dd 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:fbdebc1c9361339dd0db1051e59162ed62fa2787c46457848da5ee6a30474588 -size 106570 +oid sha256:a6cee41aea25e78f820acf98f01722062ab8247dcc78652b8ac9a27e60f24249 +size 88723 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 60ab311cdc..d254ddf882 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:7a6d3be47ab7d9234657d4d088389c8aadb4d9e073d8c91f4f81dded1b6662a6 -size 42160 +oid sha256:52018347e4361a393b130d1ab479ed96685cacc362b72fd71d46666afef0e6cf +size 40949 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 66babf5423..a5fb3cca49 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:7bb7d1c08f5b2551117aa1aff8a3afbabd0f4100e0fae11cf415bd8e851c0307 -size 103835 +oid sha256:5a56da5b32b6ee1ec6c21ec4a24420c219f1318b632fd91bb5e2d8e8d29f2b59 +size 86985 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 2b50f7350b..9a8e074a97 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:b3f70a15def31e67e84afe7f9545d280bdbca01c6dd63864662f19976df3bf33 -size 40960 +oid sha256:a96b3cd320587af70dd8dee4e285eb374d04ffc2ce8371a3cb07866f514f32f2 +size 39863 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 0252e8d778..573d02a807 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:cd023c99c75dba5987f86951d46e821d378440a35335dc46894ff127951ee50f -size 59135 +oid sha256:cd1294f7077b7d1cf6a048ecd3b51d1bb95fbb34df9c21e332dc0a7c75160ee8 +size 54965 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 e142b9451b..a2cce20c03 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:6b9f321dfd59e2a388c0d864c73eca01c29b7f96ee0481597ee3cd8ff1df1781 -size 56262 +oid sha256:e8a8b0b2e6dfae21cf74cbf48f35e4bd52c1951d0ad24fdbb9a7d958b49d68fa +size 52156 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_10_en.png index 4f172d6a06..7475ebf250 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fdd74d6df903a0cedfbfc2f30430b5802e4f0bc7b2711e20ae47d297066b056 -size 40297 +oid sha256:c8ed797426703ce250ebb4e0cd0d6753e0581a7751fc35654d12ebe4c0746a9b +size 39351 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_14_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_14_en.png index 0834071308..578db9b288 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_14_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_14_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:37ac3a032e758c4abf9ac8a87e887c7e015f2d35160d59a7e263963300bd0941 -size 31558 +oid sha256:f892ffadc2999b904be4fc72b0345524a53699386265ff446ee981f815e51ed0 +size 30111 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_15_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_15_en.png index 330bd7edc7..8c7da8b54c 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_15_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_15_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:44b9c4608696c6a690e7f00f661b3cb55d03826b11569b6e8ce0a48f9754dd39 -size 38547 +oid sha256:44f1bc19ce7f2db1d2e9ce5a0018d48848c63af21847ec238d08791999df8401 +size 34559 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_16_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_16_en.png index e9ce885cd8..1549fc3041 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_16_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_16_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f92065e3de67f39ce27ae3e321f59fa2d18b5927752dc760450ec275935b1959 -size 43405 +oid sha256:4b05cd6be3be2571745310c89448bce1562fa191a061549cd3ad0936656933d8 +size 42456 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_1_en.png index c127d5b02f..0c2e9f7f03 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:919101bfd215af97a098bdfae4980445bd1d0560f96fe9ce13a870e2d8817fff -size 34247 +oid sha256:680f7a4fd4a992e70aef33628faed82c8238109860fe87b91a417d1b880f0a4d +size 33258 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_2_en.png index a902aef055..33023589e6 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f9707b754041c871c408e12613faa186fa4f4f760ca793caf20c62bae07246b -size 32021 +oid sha256:62a10762a1eb986d6f870d82fd9f5b10803e5178ce4192c4ae6cc17889f44c14 +size 31021 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_3_en.png index 16ac399dbf..78c6876524 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ad9105ca2659f51b16b41f9317812590f38adb8431a64a139bb47f54fdf9eff -size 29754 +oid sha256:8f592039e3dcf2036bbfc7e644eddd717bb9c2fac2715077b8744a96d5e8ce94 +size 28712 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_4_en.png index 78b8500823..12dee69df9 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04180611d2a959d07bbbd051c4386f6428d841c5ea74e197a80149370db76e04 -size 41830 +oid sha256:2a707c7e9e4a1c91d12733a4002c914feb5365dad1e5fda77b4b3fd862d98916 +size 39587 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_5_en.png index 644110ef6d..0e57f43ea3 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c4a828b9aa4e4963e3cb39e6f65f82ff5e595fdccb4dc5b48db532eeac1fb00 -size 31388 +oid sha256:018a813fbd3d3bbae4627fa9acbbbde709d83d67b7292ce02c3cd170572184d6 +size 30602 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_9_en.png index cc08744b50..fd13827148 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da07e3041ec5814feb0d14e8478d0c817d1496155db2daa612048b94530df20b -size 38200 +oid sha256:19484bb21efbedc60478192da648d035334fb0adf7f6efda0977a3a25e3b6696 +size 35394 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_10_en.png index edc2a732b6..4e05331ad7 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5a6da57cc5659ba3f3705cba41fd23c7628ba2477fcfc427b8e26bde37ed812 -size 39960 +oid sha256:27c86e8f5c4a00cb13b024bd0cb1375f04d547d03dd35459c4a0853106f1a3c9 +size 39079 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_14_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_14_en.png index 867ef2ea72..f5fe9ed256 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_14_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_14_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f47c18fda5c4839e1098955f57c4a3e5b9856203c6a84c31903aa5e0b2da46b -size 30971 +oid sha256:6977342388cd3702e94d7b233dcf89ee2b0698ff1f39323c0520a9b88128a3fd +size 29622 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_15_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_15_en.png index 40257d2267..bcfe430ad5 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_15_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_15_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:112286f6a18865db2333a68cb804e930eb873bb04596bada2f825e2223b4f537 -size 37362 +oid sha256:f6993298eb428c9f79a7b837c633b20c69a8170f179bf47af1f967ca2eb28f66 +size 33788 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_16_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_16_en.png index abc81a0d58..d0a42e7563 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_16_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_16_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff86f906357d0bf611bfc9cd235e66e0247dd7bb66903cb0e5730224d5dc897c -size 43008 +oid sha256:374318e250e1b813025f56ee7121e05905421c6c5657e228bce58f60da9729f2 +size 42137 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_1_en.png index b480466344..27790717cd 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc849f3158de5cd3cdc64a57e613f3b3a5cd70794e7984d7cfa1f8f425184592 -size 33440 +oid sha256:498e344123c195dadb96125bb2fad6ff7c8647dc1205adbb1e2068820e861631 +size 32516 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_2_en.png index 9869f158e6..129a92851b 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed211c9bc258185de6ea4cc414fd214f256ad1649c26a8a28f25fd1b29c34783 -size 31630 +oid sha256:d14358f97b6a7267deae81f0ff721fcde2acf63c5baff718beb94c41c9afa022 +size 30696 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_3_en.png index 42619d6321..8a6464958c 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a88ffdfd4259a2e85b3a8375591eab3b0f12e927eb83f9c6820995acf44d79e -size 29607 +oid sha256:77f4e76bc68f099d5f28f811c004dbc48a5da17edb0576dc76761cbe85ee9bb0 +size 28629 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_4_en.png index 1b95755ca6..8093435a66 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fbe20a3d4e0c3706ce81c1f52c65ff4f749b0afd6bdd3efb4100ee1019afc51 -size 40580 +oid sha256:a7fc4c0f8964f6cde35dc18ad39cc89249e14af35a10ab7a060e213b622f008b +size 38446 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_5_en.png index 0211928122..0ceacc4260 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cff1f0a8a34cfd4eb55487540a02c70e629d7f6123355c95380498817053eb78 -size 29575 +oid sha256:1151f42b6fdb810a78dae144be8cdc1a97f4d0153c8690cbf2756b75308aaa13 +size 28869 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_9_en.png index 8af0e7e350..ecf3010c75 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0b73e8a2429db6324f471908f46ccd2f6723d59caf4acc738c07d6e402a19285 -size 37752 +oid sha256:b2e279977cd66e1be4159c788a70b8caca9cf32d7b428b0a2407105aabe9a405 +size 35083 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Day_5_en.png new file mode 100644 index 0000000000..2e7635cfdb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Day_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4eaf3d650155e9a779cf796cef116eaba0f1d6722b229332b224475393a88178 +size 16828 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Night_5_en.png new file mode 100644 index 0000000000..10ca16827e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Night_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59f062f54833df71be9d7c4e785bb01013a10642e0d863bf7ef2abd8862b93c8 +size 15476 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.onboarding_OnBoardingView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.onboarding_OnBoardingView_Day_7_en.png new file mode 100644 index 0000000000..cdd0ab890c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.onboarding_OnBoardingView_Day_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0c1cb36f30969765191d54131c862a06ec749668f79e5cf025f487ddda1ca0c +size 21891 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.onboarding_OnBoardingView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.onboarding_OnBoardingView_Night_7_en.png new file mode 100644 index 0000000000..f45558ed75 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.onboarding_OnBoardingView_Night_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f25dd05a8be436b2a6d9721080dc460f50bc7a0549885144a285a428a80a2e3f +size 20982 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_11_en.png index 0e59b57f97..d7254dca40 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e85eb3d968eca031af366d557a1afff7aa6f427f9c5761c4568b807562248fb9 -size 48880 +oid sha256:cefbe57afc5b78b598d004fa9f6cc9d42e00127ffb7900a2f5346eeee0b1531c +size 49183 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_12_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_12_en.png index 743bb72999..789df60a7c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29276b02a32bfb0eacc9fc4853711f0d43b4e9e4dc927d7066ed0d0b1ee12680 -size 50357 +oid sha256:03fbd054c197e783d2079cf4b3953f1a4f15dc7c38889dc826cb5de3b7c1f4d0 +size 50597 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_2_en.png index c4ce6ff884..4568d23092 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:494e17d6cb91effd9351b9e331ffb4d8c4da927b28bfa8e740fdb7d865bc7fe3 -size 43609 +oid sha256:34c5fd10902041682c6ab2c3022ea44977035f7de6ea9844d94876b921327ced +size 43960 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_3_en.png index d9507346e2..dca9066157 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ec6dd722d802baa463784587d4d8844edb9a38e623fffe2fc347970e437cac6 -size 46583 +oid sha256:531e376ef91fdb4f535a951222f5e25ecacc7ded6d6693e30551ec68a16dcd33 +size 46911 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_4_en.png index adadb27faf..c051c9bd74 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:204de297913cef7d13569bb8b51135c6a296ea6eec058dc1ce31ebd547137887 -size 44947 +oid sha256:c18f38af96a7f2d0e6dd30827d9ef5cae25d7c864df81ba763044f782f3bb7c7 +size 45233 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_5_en.png index 7aa0b405ed..13a4439f26 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66bb9985aee2e4988f3beaf58e7979b0812876570d5905a31406069546aaf0e9 -size 42010 +oid sha256:96472d116b4c961b4dc44e7a5e8fcf9a7751ed7662e8c4a169722ff53a53ceed +size 42380 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_6_en.png index 5437089abe..10ee8e348d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7729240a99b1fbf3caba5c480806f88f441d4c17f796808d6f50979ae76ecbe9 -size 45125 +oid sha256:055df03c64585976d1453c0c3b5b0460cefaf1aaa93a47d17b98cc80fba11f29 +size 45525 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_7_en.png index 90b6b9c5d8..05e6b2c7b7 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f6bec6647ac0678ef3420e222ea2373c4d618f7eee32347548c0fe35a7620b2 -size 43261 +oid sha256:21a5e088364ec86448df788e0d54c27dcba3c6f696a86e7f97289da0792ede75 +size 43598 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_8_en.png index 91c0d21b39..36ecb96acc 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a97331fddcbc4c89743e15bd79cc6e753b59be0f33f41de2d8cc184b3e1f3ec -size 45084 +oid sha256:e356e49f62609b0c96b294a51af0c3dba861906d0cf18492460b2d199dcd1003 +size 45368 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_11_en.png index 6fdacd6bb3..e762a68e92 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:48ca8baddefc71d98aa4060d730f7785e654ed078f5c9aa21459df5b730b3acc -size 47969 +oid sha256:a883e703a5f8c9a794ca274e52b493c490815f39c84c7c7b9c9cef348ecfb766 +size 48368 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_12_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_12_en.png index bb894571fb..67b93b5e8b 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0d19fe576a9cb4895ccae8c3f862239220b9542aa0c4c102cf5a99a0184ad94 -size 49392 +oid sha256:c44806a243336438401a21e265c8d513fff7e4f2cbf68a0c745f5918c89d639a +size 49828 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_2_en.png index 15896e77fc..ff27990665 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d81fdfb3340f8d8d63a0529eb2f4d035ebd4789aed0c98272bdbcb9c4340c7c9 -size 42544 +oid sha256:9725a74d334baddaf3946675b871abcc24f20e3518fe572f6eaffdcd5e0bf98e +size 43028 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_3_en.png index 08b5933d26..ddf220b94f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e882786b51e7692f52f1938cf1a29581ec299e4bcb7a3253ba69009140a29233 -size 45837 +oid sha256:78379d56dda45b77f6bcfbf32e12e78f72e88c760d7a0911bb9e8c9f244c39b8 +size 46151 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_4_en.png index d66e2f116b..39a4ed6170 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fbf6bda60c2f68bfb41405919d669b7aec8db408d49593ab0520b5b9afec8627 -size 44217 +oid sha256:b8213b5f1c9620cc8edaaa9971906f09c8b466d42ce253a9f52d30419af2e732 +size 44722 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_5_en.png index 54af19efc9..05aa90f6c5 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d68f839ba6d6aa3d9bd9fde6d71a68d95130011aebaf43c87eaf8ff826f48a4d -size 40689 +oid sha256:5c94f65df581c57f83346e9c8d697a50eb95cffa5ac3fc4198017a41b9d72537 +size 41215 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_6_en.png index 840cf053e8..6863297844 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c139a1a328cef4752219fd11df4c79b19c47501df26278811cb7775269988c4 -size 44501 +oid sha256:9fc2b4034704979785701cb42fe2b967256a6151ac90934cecd5b8b1922f780b +size 44987 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_7_en.png index 9140f6932e..cedfe0a891 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f17461d5b74d2c1c493e58236793978a5e96bf5bf15b2ab0179dd0b39b9e626a -size 41948 +oid sha256:d14b2d18fb60fcabb3e657001c97d95f55b82e87cf8efa34c8a5fb2ab237a359 +size 42385 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_8_en.png index 9c6ca432df..ed3e934f07 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5a920e128caf9d5609d9030e5d37cf951011bd51009c988d84873c993fff95d -size 44331 +oid sha256:5d081025bedebd0993968f3522a6e4266ecd20526284c37794a7180305d07c7c +size 44806 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_en.png index 93a249ccd9..04c099106f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af699468bfa61662538fd3b187ebb1490f6a8e692c40bd13fc44beeb26744c93 -size 21790 +oid sha256:65a59efc8cfd19f4dddf0ed9d39c4cab634ebc720c2da7737f382dd2e325ad37 +size 22517 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en.png index 54208948db..42c3854905 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d79e3b8f699dab27d180942bc8f68f0193148e0e04602c03ec7592e4b73278f0 -size 20876 +oid sha256:18b5a57b061028e9b0d265f725e5c2b08439589c778c49f054822ab3baab68bb +size 21551 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_MessagesViewTopBar_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_MessagesViewTopBar_Day_0_en.png index 571661e434..1d0d580c56 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_MessagesViewTopBar_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_MessagesViewTopBar_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:065466dc414d77a58f2d22c26fc9d5e9b7afe5d5206cdbcc2bbdc6e8ea192d15 -size 40479 +oid sha256:c74cd385b81f7f101159b0ab3adb0c3493a8ce98b4b8cd944a166e9d0a30f8a2 +size 39803 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_MessagesViewTopBar_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_MessagesViewTopBar_Night_0_en.png index 958d12766d..dd62e04e2e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_MessagesViewTopBar_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_MessagesViewTopBar_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c34b8c47d89231b8e5660726d63b94e3e8b97a2fc7e0b615f8f9e2d61066470 -size 39172 +oid sha256:b59fcb83f0d291b658efc88ce311a108655af03ca8382339c2a0d0c3a5bcdf43 +size 38201 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_ThreadTopBar_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_ThreadTopBar_Day_0_en.png index 079ac780f8..47891dd5a9 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_ThreadTopBar_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_ThreadTopBar_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:617a819de3cd223323b0fa1853185ceb26ffb97a22d79524acec0f4d5b3a634b -size 33550 +oid sha256:757f2a92a384f2084a1a1ea475e2a6743a55ce5ad7758bdf1597435ca29151e1 +size 32879 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_ThreadTopBar_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_ThreadTopBar_Night_0_en.png index b3f0e9c13e..4ad9eae262 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_ThreadTopBar_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_ThreadTopBar_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6e146eee5e94e29a2b87b3d4a9bf248445e08dc6eb68c2268b6f263533675c78 -size 33050 +oid sha256:6f56bfe0e59c767102a8e079c31e90cbc10c00a31b35614cbb188e1d5008b041 +size 32114 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.labs_LabsView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.labs_LabsView_Day_0_en.png new file mode 100644 index 0000000000..e6662e8213 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.labs_LabsView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f8fc6f61ce73823af4b4edea7bb031fb15bd600b231df1173d33bfdbd09b5000 +size 43405 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.labs_LabsView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.labs_LabsView_Day_1_en.png new file mode 100644 index 0000000000..73fd95a516 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.labs_LabsView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f1f3b861b5bdf266a4844248e54d36b7d47eb6b43aae2e877ef981b955b97b54 +size 37476 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.labs_LabsView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.labs_LabsView_Night_0_en.png new file mode 100644 index 0000000000..12fc2a9e3e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.labs_LabsView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:73a6573fa506af844b8327a6a0da53a9a1bf4e222baa1281a992290423b22c5f +size 42187 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.labs_LabsView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.labs_LabsView_Night_1_en.png new file mode 100644 index 0000000000..182e90be47 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.labs_LabsView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4f0d016343bce34896e7983877c9166ebe97e62423e177cacc0f67b467e5122 +size 35121 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_MultiAccountSection_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_MultiAccountSection_Day_0_en.png new file mode 100644 index 0000000000..caa492261c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_MultiAccountSection_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1b77e3ce009dcfac0e41e7f28e5beb934ce39ade1ca85115912a5029b46f47f0 +size 58278 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_MultiAccountSection_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_MultiAccountSection_Night_0_en.png new file mode 100644 index 0000000000..06ecd42cb4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_MultiAccountSection_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4228cd39352d4efcee45866839ff4b1c63426bfd45af083af7b2f57286c2181f +size 59227 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_0_en.png index 782a8aba82..cec26474b3 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5c95596f8a3e78692c7fa13b95a3491d320e4a89273c61cc22595817bf4e846 -size 38104 +oid sha256:c0e3bbf4ad7201f993d8ddc280046d770812a969bba407e729a4726eda04072c +size 39369 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_1_en.png index d12ebf79be..9c5abc5b8c 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7cfb090fcb56f52c935f72ec52659f574aa088a663a991c7cd49688d42001388 -size 37944 +oid sha256:90094980761bdcb3ecd10666af31f45be487cd06b81fd46710a6012610d25329 +size 39204 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_0_en.png index 3f96c2234e..6d1b50bbe0 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b092653f3c1192c3c2e0f0eb8803d20a0879af6bdcde2d51f6f6d4894ccc7c52 -size 38914 +oid sha256:f98bbf43c73a0b79c7113180e117e8a7b391d4e4f1cb358aba8d1865bec5bba7 +size 40281 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_1_en.png index 499340fa1e..22b46ce794 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78411c84878a5f97b2208b7d688094148fbf88660b1b8fbc0e5fa7b2aebbbf5b -size 38968 +oid sha256:597ba7fae13fd3bc634aca0ef93e2e3626f552225b6848a5f7cff70fa04749de +size 40327 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_12_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_12_en.png index be5bec2882..58e1299cd6 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5bc5c6fd076abe852ab8a44a204988adb2e506a6610d5b2e32199176f2783ecd -size 44120 +oid sha256:75ac80012ecc16c11b4e6267d7570e141060b01991aee50a6bb26316f328703e +size 42304 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_12_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_12_en.png index 2bfcc8fab9..1058100bcb 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8c9c5c46539bbce9874a03cbdb58288fd925bbc381a2030ca18d1eabb7667da2 -size 44744 +oid sha256:d60c7c0e5a7926142ecd2da8166708174240045a5814545114ab8e3fea5aa871 +size 43317 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_0_en.png new file mode 100644 index 0000000000..ba768c622f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c569a42e638a49554127d3dc59c1dda5306e50406a6eaf1033345b44e2fe037 +size 13941 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_1_en.png new file mode 100644 index 0000000000..b42cde52d6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b43c73a9da70133fdff8c63dd6ed8b130b76fbbc28977fae9a34733b0f64faea +size 15840 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_2_en.png new file mode 100644 index 0000000000..a6ed2b4caa --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02ea4a02c0901c3d31a86272659f251e3b9299f669177b7a46a9007159989519 +size 44313 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_3_en.png new file mode 100644 index 0000000000..78de62dc26 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a8d851c9393b9a64642d08be96b0c449ce894fdf69d52d0eb664d83c7005520 +size 44211 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_4_en.png new file mode 100644 index 0000000000..9580492e5e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78cc2ef488b13f72da4d1d149329930816e1d91541bbe62eb48c78b61564dc18 +size 35868 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_5_en.png new file mode 100644 index 0000000000..434ea3b936 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:426847168cf1cf81df60193ba69d1c02cad567f0a72c03f0b2dadb76e92f41c0 +size 42600 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_6_en.png new file mode 100644 index 0000000000..ae57be48c5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:479fb84a857b3fcb15a9bba6a6fac3f8e1309a7d5af9c10c1cfe1d16c2eb678f +size 42208 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_7_en.png new file mode 100644 index 0000000000..e2a34d1b7f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba7631fe1e5d20946256829e8414f5a305e3a2755d79fc0693fd932bfac40f4a +size 40182 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_8_en.png new file mode 100644 index 0000000000..58c84eca38 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d5be43f0ae09dfea01efa28519b003c6c3bbd8a47c8329ccc9721acdc84a116 +size 16531 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_9_en.png new file mode 100644 index 0000000000..8d62c122e7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:520698bcbc65cd2ccbc73aff01132a6b5d39c7d349a814e5a26e584cec5f2eeb +size 26083 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_0_en.png new file mode 100644 index 0000000000..4b003761fc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1935a53ffe3653f9c094996a7b62359fe70c18ea0b46a7204969f70ce439b5e7 +size 13924 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_1_en.png new file mode 100644 index 0000000000..855181d5fe --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f90882b794619797d17c9bdb8ff2e56664a5bd95ab9bee0bb59371f954c0e7ca +size 15395 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_2_en.png new file mode 100644 index 0000000000..8f6d72cc9e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4071cfff533916af1c99636ce6f61e2fbfd8d945dbba1b11e449ce55fd4bd7b9 +size 43148 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_3_en.png new file mode 100644 index 0000000000..e4a21c1d3e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:03dd230a42399b5db39a57dbde170955c7f40793fced9420e2fcf8c55820b8c4 +size 42948 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_4_en.png new file mode 100644 index 0000000000..cd30a49899 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f8d4a0d5f17cf6781d34af68ec0fae1c64cdf88d59ddf4306db411c4c67fcfd8 +size 34945 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_5_en.png new file mode 100644 index 0000000000..6038b2a50e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:26fcf6cfae843737df553b143d9cbb8049001548b0c3479e1e24a28e0c5e6527 +size 41632 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_6_en.png new file mode 100644 index 0000000000..64dab99eb8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc8a9e1d6fd7a81cf1ef718d2f3e38babcf96e58e00c2c92827cba0aadd8903e +size 40613 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_7_en.png new file mode 100644 index 0000000000..5629c9bab1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f0ce9d4bbe5a8bdadee5663e8ad8b9b189aa25b5eec7e7c1a40430c09f7d1d3 +size 38108 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_8_en.png new file mode 100644 index 0000000000..501d938326 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:16d9c0ce7ffefb7825efb5e824c06bc721f85f3ff88340abf9c41b2b4679492c +size 16435 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_9_en.png new file mode 100644 index 0000000000..dc1018d69b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ac66ddfcf3125726d2a9a7a83ef23880339f56c12bf2cad68bc969a4deb0f38 +size 25776 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 new file mode 100644 index 0000000000..79e9d22715 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b9c9e6833668344ddde682f0dde551af1f862647c7a3d749a12813d711ef2ee8 +size 34609 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 new file mode 100644 index 0000000000..64fc57b85d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1bdd937cb84e29f1f05c1d718b4350694c65d0c75c0e599effd6467793e331a6 +size 34771 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 new file mode 100644 index 0000000000..fb4dc3941a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d3ee4db00d7432da4808aeba2cd0a5409d0aefecf66e76366cf60f6096cd443 +size 35073 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 new file mode 100644 index 0000000000..753fffb205 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b095899fe3ff11e393923a8101c986eb3603778f1a4ded135c3619f6d83771b +size 64666 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 new file mode 100644 index 0000000000..fabd2b2255 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91a3e568c74bd2b7a9d99c481504025290866d0335e38cacbe7340be7a4b61cc +size 65360 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 new file mode 100644 index 0000000000..30517b5dc5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f19d84a37f3c570fb53ae28b185cbe73b4fe94f69ba57e6935ac8a078f20afd2 +size 59751 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 new file mode 100644 index 0000000000..9a8a9b4d8c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:008f4e11e7bd9cc91e46381cb0509329e2299de22b7b485e3036e458033f9bb9 +size 34036 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 new file mode 100644 index 0000000000..aedc302e87 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2270b4da2b1e55027c253422c04f31f6884cf8accf273ce08efba95fec40d599 +size 34177 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 new file mode 100644 index 0000000000..ac12a9caca --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:39afa43d6b9b8ba7d5ff83ba5a956fdfbb62510c57d34c81d65dd558e0501269 +size 34508 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 new file mode 100644 index 0000000000..885b3097ae --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6396f10b0bfb904eec898b0085af56f802c0f5a5e50af36313a31fa20a1dd0c +size 63449 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 new file mode 100644 index 0000000000..92edaf6970 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c9425dfd394a554bdbf43c021ec1e26d0e7f273f4ba89df670170c73641b51a7 +size 64049 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 new file mode 100644 index 0000000000..0807dfaec2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93ed6d05c199a886a53ecffb7a56a586f4fba874fbf18afb4975782eb2d10744 +size 57977 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Day_0_en.png deleted file mode 100644 index 47fc782a13..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Day_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4c53e7edfe8b9ea00bb97f9ab6e8289081b5c36aad54a58d4f535fc533338797 -size 15733 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Day_1_en.png deleted file mode 100644 index fe88fafe4f..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Day_1_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0075dca9fd927dc7df4e987fe5cca8d2a2ca5b268bc46916dcd49724264a3859 -size 19834 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Day_2_en.png deleted file mode 100644 index 80a6ed2561..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Day_2_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9d784e5d4d4af937a33825f7098d0849fa044d96e4cf21c7244f51432d3f32c7 -size 46725 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Day_3_en.png deleted file mode 100644 index 1c0ff9913e..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Day_3_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1a1c851207069720dd83cf32da6da80d25a4a4c40cba9884548f9dea09ca6654 -size 45383 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Night_0_en.png deleted file mode 100644 index 1de4e50542..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Night_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ea671a40116dfe7a0e4ac97b58cbbc48b3f70fe08b5613c4b21ec13bc6850c3f -size 15589 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Night_1_en.png deleted file mode 100644 index 18a49cfc20..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Night_1_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8b69241047e1d5787620331adc33f2b78084f47cf02c531a4e41011816590a09 -size 19523 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Night_2_en.png deleted file mode 100644 index bd899f7f08..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Night_2_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ae27a6a05d85587b066203ec8828db0ec1cec2171e1f3a63c6b0c26e0b71555e -size 46151 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Night_3_en.png deleted file mode 100644 index b3c914f630..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.space.impl_SpaceView_Night_3_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:067d6b499ce3ec737792a773c2959aea44829cb27a8fecbe1925ec884bc7f53f -size 44650 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en.png index f8e38d2a72..8208ba5369 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d345035d1ee105f7fa1702a3f953df99b0bfce2b0cf48cc92e6248d80690e4b5 -size 14582 +oid sha256:537781aec261b622c47345b48e8fbfd68e3cf0817bb36bf0e34e83769d6d6d21 +size 24337 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en.png index 63775e9552..7b095b8e0e 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15b1f5afddbd729f1ec01326280b11e00f2b7c84e816bd980eac96dc61db92f2 -size 13970 +oid sha256:9e5bf90db9ac805e4054c8d9c759181768a61f13bd30092bdf98fe0a962144f8 +size 23678 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationViewA11y_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationViewA11y_en.png new file mode 100644 index 0000000000..87517cc357 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationViewA11y_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2eecd4b52a31208444d14b5017e42fc31df0391bb0ead311c01c9359a7e42f03 +size 74367 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en.png index 71543a83b5..b0381d75ac 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c767fefbe84a0dd681e07c5ec88903283b2d0d4ef9d6f33232f7ef03976c96d -size 40179 +oid sha256:4aca42b6dc3864028a7896eaa5f410e68a320bfc55dbebd1662b9c080b2c22ca +size 42100 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en.png index aec01d4046..9ff94627ab 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5407076d84792877400cc065e57b0b2b71d4abc8138c295178328211e1ca341 -size 29011 +oid sha256:897c738d61c159cfe70380e92617b014947dd8d683467b741562271cbf838234 +size 23227 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en.png index 71543a83b5..b0381d75ac 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c767fefbe84a0dd681e07c5ec88903283b2d0d4ef9d6f33232f7ef03976c96d -size 40179 +oid sha256:4aca42b6dc3864028a7896eaa5f410e68a320bfc55dbebd1662b9c080b2c22ca +size 42100 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en.png index 3319bf23d9..61dcd44038 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bdd93fd698635ce7be6667280182994e3427cd0701d283cce163ec706e88ae95 -size 36420 +oid sha256:f0d95c8515dfe38a3de02fb68a035e41bedac03abadae44e371a85e6f32c9174 +size 38460 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en.png index 6f05eab9e5..3accdf3380 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78507911cefb2a6c7ab9b0ddf1a7a55bc4c13adadbd3a60075ca7eb89969a8b7 -size 32351 +oid sha256:74ecd0909c13895cd8d2e3827fb05fba98bd61f315735b281fd5502a9ae02bd2 +size 32354 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en.png index b6b5c102d2..bf8e3849c5 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e54c3911419f23bd99fd879d0b6339cb09cffed560a33aeded5b45e30602df5 -size 46617 +oid sha256:a832cdb8459b626779d19424282b52624e3e76c1943d0e5131049f27fe45e0d8 +size 46376 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en.png index bf207c9c12..39eacc3707 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d83764f58970b1c90bcdcef4bcda989c762380cc6eb423101d1abfda9ea7fe2f -size 40295 +oid sha256:a5a1132f3b99b4a047839e752ffc6675df91f5cdb750625ac6a15483b7a376a2 +size 40058 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en.png index beaf5aa590..c1cb6c04d8 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e23231089f2a3701070fbf279aec7e68febba7cec643a57d2d0525d4eb6e2d9 -size 39087 +oid sha256:5a9c13276724d2343111a065f4f6a3798206f6bc6016dc5314c87448ab74efeb +size 40891 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en.png index d97ff31e63..00fa43c729 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:000b932dfe770251974ef682e329769ba4412ffa91e46cc5151a16399eb34eda -size 28446 +oid sha256:ada343f41dd5f20ddef032216f462d851aff0d9a94f7545e72062e8b316683f1 +size 22570 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en.png index beaf5aa590..c1cb6c04d8 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e23231089f2a3701070fbf279aec7e68febba7cec643a57d2d0525d4eb6e2d9 -size 39087 +oid sha256:5a9c13276724d2343111a065f4f6a3798206f6bc6016dc5314c87448ab74efeb +size 40891 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en.png index c9137d4f70..7d7faefc03 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c7b951739149e45d35d7dd2a03893c2471b37d37f5a70e98e14f225697e80a4 -size 35609 +oid sha256:18e41a828de1e73f0909449c096bc7d9660e5b677075845657267db731b913ab +size 37597 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en.png index 26386330fc..43f97a426c 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f268e84b9d477429d817fc4b80ff99fbaff45434e236bae0ce05d656b4337675 -size 31620 +oid sha256:6242746ae0ba35d6e2e4a9c1d4c2a5e9433015eb7711767342afe4386edecfa1 +size 31701 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en.png index d7f9669419..4ea7e1d206 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:33c3a1f2cc919fab50dd20351c69e5d81bd913f316bf6e6c6acf212e64433c3e -size 45581 +oid sha256:1d512f3d1029d30265688b126064ac79fddb295fe7cc14f1e3bc198eca9788ab +size 45377 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en.png index 86eb0abea4..3d570edca4 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e4abe4b9891455b398be36822531749989569144a7ac4e810bb596aabeac2dc -size 39521 +oid sha256:05710ee6f0dec48f80a7e2c861b3f56208a707bb20ffa94878b56e855dc56da4 +size 39302 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en.png index a9883b14f4..f721955fe2 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8b6abacdc676b6920df40aedae9dd0792d5a5dfa61e67e5a97429f3ddd38073 -size 27798 +oid sha256:061efadad844117ed72a1bf22e2c61687bc01a93a8e8f290a12b48410713c0f0 +size 26555 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en.png index b6b5c102d2..bf8e3849c5 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e54c3911419f23bd99fd879d0b6339cb09cffed560a33aeded5b45e30602df5 -size 46617 +oid sha256:a832cdb8459b626779d19424282b52624e3e76c1943d0e5131049f27fe45e0d8 +size 46376 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en.png index bf207c9c12..39eacc3707 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d83764f58970b1c90bcdcef4bcda989c762380cc6eb423101d1abfda9ea7fe2f -size 40295 +oid sha256:a5a1132f3b99b4a047839e752ffc6675df91f5cdb750625ac6a15483b7a376a2 +size 40058 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en.png index 89ba6e9fac..1bc1cba469 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9cc65aa4aa06e78016a3fa384d2cd45722bc41abf1de888071fdba400e87b792 -size 27140 +oid sha256:83c1691efa952ea64b00fc794bb082b4975c037d8ada71053a352528c688cd8a +size 25903 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en.png index d7f9669419..4ea7e1d206 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:33c3a1f2cc919fab50dd20351c69e5d81bd913f316bf6e6c6acf212e64433c3e -size 45581 +oid sha256:1d512f3d1029d30265688b126064ac79fddb295fe7cc14f1e3bc198eca9788ab +size 45377 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en.png index 86eb0abea4..3d570edca4 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e4abe4b9891455b398be36822531749989569144a7ac4e810bb596aabeac2dc -size 39521 +oid sha256:05710ee6f0dec48f80a7e2c861b3f56208a707bb20ffa94878b56e855dc56da4 +size 39302 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.ui_VerificationUserProfileContent_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.ui_VerificationUserProfileContent_Day_0_en.png index 866ac11528..d5cbd5e217 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.ui_VerificationUserProfileContent_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.ui_VerificationUserProfileContent_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:84870e3b5554f97ff8093286bbf246b74b3bfa787a8318304740f55492809721 -size 12397 +oid sha256:43836f421a5f9ab68e4a3f6b3430a6d3daeec9f737bc1b20940a3fc9597057c3 +size 12451 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.ui_VerificationUserProfileContent_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.ui_VerificationUserProfileContent_Night_0_en.png index cd0a2208f4..ee9175a640 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.ui_VerificationUserProfileContent_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.ui_VerificationUserProfileContent_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72e7183662d01486545e6702faf22dd867cc47ed72a50b8cd2c24a307cef58c9 -size 12408 +oid sha256:053fe3e5699e98280bd40008a26f92a64e34c0f3bda1a52434137745e1ed9cf2 +size 12360 diff --git a/tests/uitests/src/test/snapshots/images/libraries.accountselect.impl_AccountSelectView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.accountselect.impl_AccountSelectView_Day_0_en.png new file mode 100644 index 0000000000..17cefd666f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.accountselect.impl_AccountSelectView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:215df53e6287ac32df6ec3a4b910a40035c8a516e89bdfc7b23aa1d45320ab78 +size 8285 diff --git a/tests/uitests/src/test/snapshots/images/libraries.accountselect.impl_AccountSelectView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.accountselect.impl_AccountSelectView_Day_1_en.png new file mode 100644 index 0000000000..815ac1db6d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.accountselect.impl_AccountSelectView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:848f7d81f4bede07d697ad326a0d23845c8446ad49cedc81e985044ad4b45a5c +size 49048 diff --git a/tests/uitests/src/test/snapshots/images/libraries.accountselect.impl_AccountSelectView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.accountselect.impl_AccountSelectView_Night_0_en.png new file mode 100644 index 0000000000..46a95f8bd7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.accountselect.impl_AccountSelectView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6bfaebba98b5b9bf083e5d29eb9a2a835ce774e26956b582d01b54a1799e507 +size 8172 diff --git a/tests/uitests/src/test/snapshots/images/libraries.accountselect.impl_AccountSelectView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.accountselect.impl_AccountSelectView_Night_1_en.png new file mode 100644 index 0000000000..8a250d9439 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.accountselect.impl_AccountSelectView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87003f7f0c981f0fee9f0773914e2fd99e18f1001045c13f52bc33dfba2905d6 +size 49941 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_BetaLabel_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_BetaLabel_Day_0_en.png new file mode 100644 index 0000000000..9957d3f2a8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_BetaLabel_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6453b165bca4b509d7a2e1749e0534f5f12465de3b32bb0513cf377021a57a5 +size 5080 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_BetaLabel_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_BetaLabel_Night_0_en.png new file mode 100644 index 0000000000..abea5053c1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.atoms_BetaLabel_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2431e2c666af3353ae85d9a101d8dde1378b4fa3d1828ff23a7f4753f6fb2725 +size 5112 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_0_en.png deleted file mode 100644 index 9632b382ee..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:40e4dba415e582c29b9798ec1a71bcd1503ba0dc32327cdc17a165aa2a93d288 -size 16446 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_100_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_100_en.png deleted file mode 100644 index eee5b83831..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_100_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9640e8e6d758a03f995c7fbbb6c3c123109594138a7cc53d5416ef5e3e157693 -size 18006 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_101_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_101_en.png deleted file mode 100644 index 70c84280d6..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_101_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fbe699fb17947981c53d47a9570da71e2388ec493a3a9471aaa13a5405075069 -size 23421 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_102_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_102_en.png deleted file mode 100644 index f4e93c12ae..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_102_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8c6688901f89d3858ec67dda889fe589fb6b59202c64a4b700c2aa484e19f4cd -size 17606 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_103_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_103_en.png deleted file mode 100644 index 53dbe79213..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_103_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:26b9908bbd388321444037dcc1aae55037d3dabc7a9f9b14c39ba871f4f9d593 -size 16128 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_104_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_104_en.png deleted file mode 100644 index 9c9b3d4717..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_104_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1a96b6d95b8941d80035b309444b9eaa038098fb16aa84dec209fc3ee215ac9e -size 21687 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_105_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_105_en.png deleted file mode 100644 index 7f55d277d5..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_105_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8da97746633081690a09a79e3f9974257bbe0f292ad5b8ee2eed454c1273c01e -size 19747 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_106_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_106_en.png deleted file mode 100644 index 91a2bc23da..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_106_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1aaa84f80b3691ed83308966e53deb8dbc97c7943d2f68a8036a6ead603ea627 -size 18270 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_107_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_107_en.png deleted file mode 100644 index a2cd42670a..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_107_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ddaae721cac21a188dc3862e3e14e80eeb13c92826bccfb8364ecbd72948d9bd -size 23653 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_108_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_108_en.png deleted file mode 100644 index 885a613f50..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_108_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:207d569640554b3b11cadb03a231ae0dfd85a3c470695cc8428cb2e295d98776 -size 18858 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_109_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_109_en.png deleted file mode 100644 index 4279c36d24..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_109_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:68ae347075a6bef936e9c905a12b6b561a01737ab886436d73f4987783a1b45f -size 17541 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_10_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_10_en.png deleted file mode 100644 index 9f89b099fc..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_10_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2be665d2bf592c4e2bcf08faaff3c9008a7d0c98f21c1553a67371ae79a20313 -size 14526 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_110_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_110_en.png deleted file mode 100644 index 1ff47d58e0..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_110_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8caa1fbeb5250684363a21549cdcfff60489059c9d1f0ca115503e63675b3511 -size 22394 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_111_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_111_en.png deleted file mode 100644 index 7946dc4e91..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_111_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ce1aba18ea8a6b45d9c40de9af961952e261c7c6200ab13cdfa67906d995208b -size 14888 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_112_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_112_en.png deleted file mode 100644 index 0b12b366b9..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_112_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6a4d295ae71ba2709845312b84983b79348069ded46b30091b9fa769a587cbb7 -size 14337 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_113_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_113_en.png deleted file mode 100644 index 556cb141b0..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_113_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:da8f54591a52475c6f47fa083a1bb397e1164dd3e735562388fde4ff45644150 -size 16275 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_11_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_11_en.png deleted file mode 100644 index 26e18f67fc..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_11_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b25d1692a779ecb4ce1875f12176b0fbbbbfd0b5917c4e215fc612785cb4e008 -size 19158 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_12_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_12_en.png deleted file mode 100644 index 2d919b097f..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_12_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:98a3562697e9d2d7f60a61976a8261b934dcfb75d195d9cf783f6cce4171dfc1 -size 16643 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_13_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_13_en.png deleted file mode 100644 index c2db38fc0e..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_13_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:49ca1d5ef80f7572378ef40e7d73b1b669bcac515b3ef54042a7b67f9c3280d7 -size 15403 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_14_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_14_en.png deleted file mode 100644 index 0e85620594..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_14_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bc5b79a8448cbbd9dd73dc416c8b6d051e335845da2caedcdb9fdc3414c4f9c6 -size 20024 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_15_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_15_en.png deleted file mode 100644 index 70fbf9958f..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_15_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:526485d4efe3b100a450885ab3620f180e587cd9fbe6e8a373032db14483a90b -size 19284 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_16_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_16_en.png deleted file mode 100644 index 033d7dc4a3..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_16_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f52dd1f56c484022be9b2d33c7647f2b4942096fc41928c8021dbb4b60588f81 -size 18429 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_17_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_17_en.png deleted file mode 100644 index fddc99a32d..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_17_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3a1e82c60b70e7755a1e514d69c0da3c7a3ef93e8528f303e9478eb880c2b51f -size 21531 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_18_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_18_en.png deleted file mode 100644 index 87bd8ecde1..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_18_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a3e85c12d1152986e307ae40753453f5caf789bbfdc00bbc084dbe08f744f4e9 -size 17424 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_19_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_19_en.png deleted file mode 100644 index 3fb58209e6..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_19_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c2c7116dcf6b9ef6a7281b9dccfdfa92589869e897d7d9781c78c3f422d75533 -size 16118 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_1_en.png deleted file mode 100644 index 1e2950faad..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_1_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7a40433b132ac5e8c21813f6202833f0ba6115f888eec0ae9bd7ab0415f7b76f -size 15705 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_20_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_20_en.png deleted file mode 100644 index 60da20ee58..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_20_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2cb816c3e37b3a8c1e0d31a2d7ec05344e7bd16ccd214bb15475754013f0927d -size 21038 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_21_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_21_en.png deleted file mode 100644 index cf80fc6a8c..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_21_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:33e206508e839a7517f0621a79571e52c61b225f57efab0379a9bdb510634351 -size 19053 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_22_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_22_en.png deleted file mode 100644 index 7c518a18dc..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_22_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d896fcf527b98ae449f52bdaa77ef5a230756e3723681afdc549f7aa1ba11c42 -size 16867 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_23_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_23_en.png deleted file mode 100644 index 012289346d..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_23_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ed5b27869ebf46171cbedb8c57d0163965d77d362445d1edd2ae307371c756ab -size 24687 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_24_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_24_en.png deleted file mode 100644 index d00929d9af..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_24_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2ef22b332b3383732def98b137c6986afcc3414e5e7bcf5b568956bd8f1b5c8c -size 14443 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_25_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_25_en.png deleted file mode 100644 index 2dcb1b35fd..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_25_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2ef70634dbed00e908fb43116fcb8d6e2c7084f90f36cca32b83498cf07e1f6a -size 13606 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_26_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_26_en.png deleted file mode 100644 index 25626e212d..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_26_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aaa1497201d95eb9b39dba91326c179f0d2e1a58ea70772067d46e0e7dcc0d0c -size 16677 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_27_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_27_en.png deleted file mode 100644 index d4d332376b..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_27_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f05acb090eb967a126bb1c4741f763bc7ba254e2d32641d7f6b7d0c3888f5f44 -size 16522 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_28_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_28_en.png deleted file mode 100644 index 9c52eff9b0..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_28_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:408498059aa91ae83ec3617172dd1af1ace78c99b172c3da1895d125965a4bd6 -size 15272 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_29_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_29_en.png deleted file mode 100644 index 8c2a67e4f4..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_29_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:884eb2d69af3e1d91fddcbcd7173ac547530e5f3439ad2edc3df6cdbbc5ac312 -size 19914 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_2_en.png deleted file mode 100644 index 08b84e2044..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_2_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3353248d6a4b209e9d2d9053dfb83f68e6e2f7de1172a80ab10cfc6fc36af612 -size 18387 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_30_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_30_en.png deleted file mode 100644 index 196009c9b9..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_30_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:35db9f05aa3c3226c611cd83b9f6f9dd72b70c9b6bb4f68ebb8e0c596b30c99a -size 17591 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_31_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_31_en.png deleted file mode 100644 index b737f4f7eb..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_31_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:74de00644b39d999afc93936939e3ff2066f5b9b526d59bad20c50e318ee765e -size 16280 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_32_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_32_en.png deleted file mode 100644 index 6a7544bd7b..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_32_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2e4ac9b255de3602f03a854b8ba71703176253a5c2eeacb364a6c87e89e8f193 -size 21200 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_33_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_33_en.png deleted file mode 100644 index 59021750d6..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_33_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2c4335667e4c604526427826e5d56a09dad85a423844f974f55e6ebda17d42a0 -size 16617 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_34_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_34_en.png deleted file mode 100644 index 329801a74d..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_34_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:27df1354079b41fbed84791777d9e54198c3576418f6d5027b4b88ff22454584 -size 14821 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_35_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_35_en.png deleted file mode 100644 index ec92a214e4..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_35_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ad1df854d799ccb28358aa5a7c61a7ec4627b6b766813c9d3a3a6b7d423c5a1a -size 21401 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_36_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_36_en.png deleted file mode 100644 index 06ba756fbc..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_36_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2d07f11e284078387bf9ab84e6b79fd35371486ba79fca7ccdfde6e316d7549f -size 14371 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_37_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_37_en.png deleted file mode 100644 index 4c0dfae73b..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_37_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:05090ff82f6f6fe27e9a2986b47c4aae72c28c0335532534d9a4b65b00613d24 -size 13634 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_38_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_38_en.png deleted file mode 100644 index 4046107dc2..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_38_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:732c17b7a00cfde8e52aaefe9b02bc009ec840d2cf72adbbd9bbab010cc2b12b -size 16295 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_39_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_39_en.png deleted file mode 100644 index 72cb85f820..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_39_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:005c5f7cf69f2452d6fe894e02ae030aa2d6692181b0e94552b33064e755fbec -size 14999 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_3_en.png deleted file mode 100644 index 3f7078d7da..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_3_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4e1dd127d87c617a4e3369033339a2adf82fed2be70c2db6707433c60f3ac338 -size 22212 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_40_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_40_en.png deleted file mode 100644 index 390215d29e..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_40_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e9c435ebe2e1c7b729c96e75ffa57e488b157a0e7632be7123cb0f4542770a1d -size 14259 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_41_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_41_en.png deleted file mode 100644 index 9ade25cdb2..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_41_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:00680313760f229b844a3109530e9c0691b3b02917b3f5e18ac0c8b07b09724c -size 16914 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_42_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_42_en.png deleted file mode 100644 index 7ae4d9bdba..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_42_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:989b61bfa802cbf66168c6484366b85d0f683416c0b8ad6c2c59b7a45ed19313 -size 15436 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_43_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_43_en.png deleted file mode 100644 index 32f6867147..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_43_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fe2ac65f4be2460a563a07cc64e7b592efb850c9a8d72c26adf6d326bdd200d9 -size 15094 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_44_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_44_en.png deleted file mode 100644 index 795d04f314..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_44_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:33b2f35b4be757434e86bce5062551a5ef5af4760016c1751b984e333342841f -size 16373 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_45_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_45_en.png deleted file mode 100644 index 38de31dfeb..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_45_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3d5ed3a4c0340d904404c1ef68e7391e1303f865d15e88de6f82ed68e8dc4b4c -size 19463 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_46_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_46_en.png deleted file mode 100644 index 7024a8cc93..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_46_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c44464ab02dd9f1fc0624f2110bc9938c8aa8856bdad811aafa05eff2e144e4a -size 18884 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_47_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_47_en.png deleted file mode 100644 index a9d29c9fc3..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_47_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:57145b520f6702934571122f4c243f8740658847c0c60e6f901179d1d420d16a -size 20857 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_48_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_48_en.png deleted file mode 100644 index a50251b367..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_48_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fd18a15bc49e87b8abc9231a13771a0fe34003b11fe1fd48df2d91f54bc40cc8 -size 15564 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_49_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_49_en.png deleted file mode 100644 index 2ec96aeb8a..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_49_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:388a1ac9f1790fd305fd329b73ae621093d323db3fa4d4d32eef92f8dd5b51ec -size 14824 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_4_en.png deleted file mode 100644 index c85c226e57..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_4_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c38215278b240bcd85628e00ba7aa80e7b0729364d936492461dbe5e7a00da0b -size 18931 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_50_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_50_en.png deleted file mode 100644 index c07dc719a8..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_50_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bc90e78d2fa94425322895028f1156bfda196ee2a761231920c6ffd80f02984b -size 17512 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_51_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_51_en.png deleted file mode 100644 index 33fa6946a2..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_51_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:74fda6437495995876f76dc1ff0e056de4937ef8d719b3be1ce73baccd31516e -size 15889 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_52_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_52_en.png deleted file mode 100644 index 2a2ed97c47..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_52_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:04fb2f230a09f0d3e27e151d98aa5d91e416b5ca9637b4db5d8e00118a68e7c4 -size 15166 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_53_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_53_en.png deleted file mode 100644 index 233b0baf35..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_53_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:896f698ef11d8a0577baf0a81cb54cabc307d95d05c230e5e3bdde40c3dc0900 -size 17844 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_54_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_54_en.png deleted file mode 100644 index 20cd179751..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_54_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cf264b6f8af5b7432511d595ff8c663c5e2be33c9f85268627f5188e3f0f8db0 -size 18949 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_55_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_55_en.png deleted file mode 100644 index 304c73c954..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_55_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0bbe1e3e8e1ea119cc61509617d3fa8e2bd047f619c41271901c73c46be1d610 -size 18201 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_56_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_56_en.png deleted file mode 100644 index 397c9c40c1..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_56_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7a7c183808801645e285dfba563c036c204a347de20b4b1e40fcfeab29fafb7d -size 20876 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_57_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_57_en.png deleted file mode 100644 index 559903c1ac..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_57_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a48eb99f4466e3a883c0c7441006f201e0db22fd0e920ad0a77a8512637e01bb -size 16445 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_58_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_58_en.png deleted file mode 100644 index 42cab93666..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_58_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:895ba9a35b8854d5005fc0671a7e6cbba5e26525b4a8ed4fa0fb612432caa04e -size 15205 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_59_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_59_en.png deleted file mode 100644 index bb704942db..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_59_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:98baed819cd8b08085ceabf7dbccccc77b0fdf0d28a3f852879f6f8aa02ee441 -size 19848 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_5_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_5_en.png deleted file mode 100644 index 432650980d..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_5_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:091da916a8a14b6ecfb2706df946bd23186bb2ad8df4d54e75ff9ada992484e1 -size 30014 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_60_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_60_en.png deleted file mode 100644 index 7f42bdc985..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_60_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c4678c15b608547c255d5eeb128c144ae7a3c5a21de2b047fc77f15364822ac2 -size 12847 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_61_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_61_en.png deleted file mode 100644 index efb2ef5625..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_61_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5e30395594c1a35d90fc0a04af340eb79cbe686d90856414d0024b769f86e89d -size 12507 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_62_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_62_en.png deleted file mode 100644 index d49a629afa..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_62_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:60362244c7adf3644ed2ae354643e0525aad2a4454f6b13abce8fd265f4987c6 -size 13777 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_63_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_63_en.png deleted file mode 100644 index c7da5ce09d..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_63_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:73de8ee7933c1a45a266761b14e09b008a62dc69b1600bbc69b8a01370fb97c1 -size 18641 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_64_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_64_en.png deleted file mode 100644 index f9e9999e71..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_64_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b62b7dc22bfc727cfcbf7f6a19683bcf4da116c7192f9943f5addd11c39fcadb -size 17018 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_65_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_65_en.png deleted file mode 100644 index 0f4930cb81..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_65_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c0855112283bf797bc846e1982c6293e321361368481a803e12658723aaf8409 -size 22988 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_66_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_66_en.png deleted file mode 100644 index b7e63845b6..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_66_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:049b3ac784a8e400bad374a960ecb77d7f4bca81079be764dc74cb161f7a1093 -size 20880 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_67_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_67_en.png deleted file mode 100644 index 517de9d5bd..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_67_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:21d83d719bee5fd19ad090791c7848890d87e4bd9eaddb17c9da1773ec81667f -size 19249 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_68_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_68_en.png deleted file mode 100644 index e39f788f2a..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_68_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:edc743a5cd067eb13e464f27c2f12f95433bd020dd1b3e920614ee913e06e476 -size 25008 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_69_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_69_en.png deleted file mode 100644 index 71ba87addd..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_69_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:45e6821d622bfef22cc41a040e12e2b49e0bb50a88b0a143bdb25e3368809412 -size 16496 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_6_en.png deleted file mode 100644 index 1bf67b7c98..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_6_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8cb143e42d0bc07652d74c74ba641a5332ee9bebb071fa5efba59ecacad04be5 -size 21857 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_70_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_70_en.png deleted file mode 100644 index c00f6f274d..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_70_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6ddd2399d3d701fa962dc5683e054c2c4829ccd99d04498415ce88487f5e0ec8 -size 15760 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_71_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_71_en.png deleted file mode 100644 index c6917d9519..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_71_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4293d98ed31f87c59ff5741344c2e41f58ff8f19c9342f6626553e978dbfc674 -size 18429 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_72_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_72_en.png deleted file mode 100644 index 439c8e754f..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_72_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:37d1e302d4a610aaab7cfc6a15572b7eb92b059d9384e7bbe4daebcaf2da9049 -size 21343 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_73_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_73_en.png deleted file mode 100644 index 19347bbaaa..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_73_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:536d8d899945483352137d23356f3777db8e2225288f01e8cbcd1a770ca7f3ee -size 20494 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_74_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_74_en.png deleted file mode 100644 index 3a5d6c57ad..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_74_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:542b080ae9374e4f6d57689b1e1b6a84a0f81f017bd276bbcab43b9659be4335 -size 23534 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_75_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_75_en.png deleted file mode 100644 index c116d68c19..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_75_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aeb02390258eade3ceb8c437bcf3592f3dde463d6f1b2a43ffefd61abf4184c4 -size 17236 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_76_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_76_en.png deleted file mode 100644 index 04ec60caeb..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_76_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d5c6bfe22fb71a9c6353718c2124000245182359ac1c43dfc8dd9b91415e66e6 -size 16385 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_77_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_77_en.png deleted file mode 100644 index e74e8a07b8..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_77_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:408f90e8a2f4dcd0716118fb522e5cd4adba0b2a429723fc2e47555b82acf005 -size 19501 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_78_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_78_en.png deleted file mode 100644 index 84b6c56c0e..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_78_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9778edbbb33ab0bbaa0072bd4659488937efe0f8b8fac0a5b64497fe6a1e28e2 -size 20838 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_79_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_79_en.png deleted file mode 100644 index aa9b310163..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_79_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:97f43860bf9e65c7822943f8806a0642ce59957be5e35650e2b69a2c562a694c -size 18637 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_7_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_7_en.png deleted file mode 100644 index 3db35531c4..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_7_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5eecc64fbbf67e53578ba6c974905c16d000a5dc8b2e0fa99c41e46dea36424b -size 19651 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_80_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_80_en.png deleted file mode 100644 index 3e54c2ace5..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_80_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:77e71b006b71300ccb1005ad3cac6a93ae242ff03f2936fdf9d10c0604d20faa -size 26121 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_81_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_81_en.png deleted file mode 100644 index b2eb2208d7..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_81_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e744d3fe3315dc6cb5beab80dc29d565500367772339071575b6505370e47207 -size 14771 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_82_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_82_en.png deleted file mode 100644 index 5dd29ba5c0..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_82_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:448ea365e3e75059bc888961e3454562d35ab116c75b3b9855723b5fc1480d92 -size 14028 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_83_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_83_en.png deleted file mode 100644 index 4985f409fd..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_83_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ad98105fcd6d6c02e5ae036ad49770b4aa35d94e32ab3a63f040deccc2375e6e -size 16703 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_84_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_84_en.png deleted file mode 100644 index e34b1d369a..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_84_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5b8e747d51d6a6ab1ab6ff80224d0241a16f38b39e0c156dc6eef47ea56eca63 -size 18140 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_85_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_85_en.png deleted file mode 100644 index fcf2962f34..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_85_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:34f28c6dbbe094b9ba070c6f1ef4b10b2625ea094629b6305dff70bdf6367ddc -size 16894 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_86_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_86_en.png deleted file mode 100644 index 8e996d615c..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_86_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ccfe44da86dce16ba4816cfe2029c05d317fe29ca3bb816278f2bd929298d855 -size 21532 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_87_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_87_en.png deleted file mode 100644 index 54558de104..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_87_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e5b77bec92bc7c4da3fdc72831797d685bf042131c2222351cc7c852da48cd60 -size 17662 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_88_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_88_en.png deleted file mode 100644 index d36db47fcc..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_88_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:99f2b0e2fda05d6fa16c0f2dcc52e92f6fa1c656c6d84cf03e8507a5c14104e8 -size 16932 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_89_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_89_en.png deleted file mode 100644 index e0660d56c3..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_89_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e79cb41e125705af70990dec3e3fdc0a0202ec8d62f2077eb2a298243ae2e94d -size 19624 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_8_en.png deleted file mode 100644 index 3f0469f5cc..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_8_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c425ffed237de0e71451baf51f674051c3ab6481ecceeff15c7f2f73193fd3ab -size 26866 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_90_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_90_en.png deleted file mode 100644 index bc91269c49..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_90_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e0faccb345f65a543c56e4db44714614915a62b6916a77a0f4adfdae5215311e -size 15128 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_91_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_91_en.png deleted file mode 100644 index 259e239462..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_91_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5d626da2924325c074dcd2baf1c112430399553a9ee652cac03b0b1d3db4cff3 -size 14393 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_92_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_92_en.png deleted file mode 100644 index 2082b4df3a..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_92_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2be7c565d30e9f45a583b93ec948a7cb35a4bc872c4f479bd1efe9eda8972e7c -size 17044 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_93_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_93_en.png deleted file mode 100644 index 6dfa192fb9..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_93_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c28eae20f614eae53e09aa4e396792f36ee1478c8141faad788fe397502da237 -size 20671 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_94_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_94_en.png deleted file mode 100644 index b740d5c979..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_94_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8ada8d054436b15e9029150eb99e78103050f8d0428a1c78ef5667bfd54d3be5 -size 19195 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_95_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_95_en.png deleted file mode 100644 index 6b06753f70..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_95_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7586db3dc8687c5c5fc73d28dd968041b192c160286f6a7c881eb7d86a2d1bd2 -size 24477 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_96_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_96_en.png deleted file mode 100644 index 42b0241e10..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_96_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2ef75decb9f501eb6c1941e212e1b330203c16458bfc2e10bfac3d197b091a35 -size 17116 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_97_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_97_en.png deleted file mode 100644 index 04732e27fb..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_97_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4940205ca9411e3175d24d7a45a49df77b2187318d4245d902db75230838e26f -size 15869 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_98_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_98_en.png deleted file mode 100644 index a48e1ad7d3..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_98_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a1faa9029414b146707f2cd0d7c4899355cd196c3b5acd6e95ebe0d0bb599003 -size 20504 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_99_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_99_en.png deleted file mode 100644 index 3eeb6f47e4..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_99_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:90982bf0b723b89c9b070c1dc94eb5a9fcf66623c382d496174f026e053929fb -size 19493 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_9_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_9_en.png deleted file mode 100644 index ed22ee6d65..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_9_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:001c22e7f7926e344bd004525ce3afdc402c87135ac032b68e9dcf5f26a5480e -size 15777 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_en.png new file mode 100644 index 0000000000..202904a991 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2b6cc7f1830927a03e32813ebd9868784663e9d7024a7ffb85fde33e2f585eff +size 93540 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_SimpleModalBottomSheet_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_SimpleModalBottomSheet_Day_0_en.png new file mode 100644 index 0000000000..22bddb6738 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_SimpleModalBottomSheet_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d27d23f9e54848b07eab23fb4b3a865a1e4036255c64fcf6cb944466e77fb4b +size 27306 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_SimpleModalBottomSheet_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_SimpleModalBottomSheet_Night_0_en.png new file mode 100644 index 0000000000..42e92893a9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_SimpleModalBottomSheet_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f2e4d519448d1a5b0c67d7b808292f60ad6391a314b9946cff89aef8fc29313 +size 25914 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_AllIcons_Icons_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_AllIcons_Icons_en.png new file mode 100644 index 0000000000..1ca5f1c742 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_AllIcons_Icons_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c327e27f5b471a9413399818482be76bccd87fd5f2a9c53e312d8900587b069b +size 102657 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 03b2c21087..4c16bb72ca 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:0fec2a25db38f9c65e3fd0218fc5c9937a017b762696dfd7be2906c7cbc28336 -size 19111 +oid sha256:a4e7d8316854658fca2e0edfc22bd844a7972543d0049cd751de31767ee1cc2d +size 17325 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 a4dc748f4f..b17f53e338 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:5412ef6dc7306f203649b777cb43ce0ef011e65640e84f14b12dd3198f68b992 -size 18204 +oid sha256:8952a3e1251c854f7b59990ca41cdb11c2ee0f36545e20237057591b5d5cfc58 +size 16481 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 7a7bf57ab7..7bf24eff1b 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:21acaf9ea606378f5f1e83795e41bec9edc2da05143efba04b70f67cce2c80b0 -size 61626 +oid sha256:7bfe638b75f253649e2d591bf174c9afa176c400e8826ba30f7e1eb50ad9b69e +size 60450 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 e76102c580..78271cc12e 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:e20d4dc80e8f983516cde0361333f941818cade431fb38ae318cfa9a589ab0d8 -size 60846 +oid sha256:f958cae6847bb68aa751cb79d3ceb0c2ea6d112f21a60d8c19633e7008a0e724 +size 59606 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceInfoRow_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceInfoRow_Day_0_en.png index 7138e2aa39..c5c9ae1eda 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceInfoRow_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceInfoRow_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0e4684cfbe7611d6c68879ddd7da5c57aa542b9f8b9250caf5f27009cabaadb2 -size 22353 +oid sha256:b20135fb6973d9d435fe1bfebb654d23ff2e1487b3863387c0843a5df63a6d28 +size 22858 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceInfoRow_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceInfoRow_Night_0_en.png index 38e9551bc3..59647a9419 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceInfoRow_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceInfoRow_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c7ad1861890262a67664d4e3b3e28d8a5d9ac4556ad029d1d5a387f61ac513f -size 21275 +oid sha256:3ea661953786398d16100c2209c7549f0dd0798f452947b7f74204319fa4e799 +size 21626 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en.png new file mode 100644 index 0000000000..53e8c50c72 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6cb722a95b3ee29f751d053ea1cab634c3181b07678449600a2c23a97de0fcd1 +size 16496 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en.png new file mode 100644 index 0000000000..2268d59ba5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:684f50c937b0ab7413cdff653a11ed11796583bf2182bfc1bbc1725f42d3a0ba +size 13073 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en.png new file mode 100644 index 0000000000..a8d5daeed1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:333ae4e1f2e0ad7df938dfa3f0b54137ed641d327bcd7c316965fb4b4cc049c7 +size 8937 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en.png new file mode 100644 index 0000000000..b90c8c5ba1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f6b5eaa4a84fc70cab57d37737b390583d3af9f6e829cecffbcf36351bd40e2 +size 23562 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en.png new file mode 100644 index 0000000000..eeafaa0dab --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f271f630d93fdf49436509d08276b6a444db92c06fd06129af93cffbb4623d4 +size 17931 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en.png new file mode 100644 index 0000000000..450015669c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a35bc50a8834997cf3bfd3884e82297e4f27d95462e23997e1887e8bf782c59 +size 13535 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en.png new file mode 100644 index 0000000000..770acda2a0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ad1cdaac2d2f1e42e3ca1b17c88b7c9a2ead134a7004885a3c3b3dcd41b13ce5 +size 33645 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en.png new file mode 100644 index 0000000000..5ed4c60ea8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f5425b12bc2902847c5b3b7e4b4e727e2e67f9a776cbf059ca6ba4777f81696 +size 38569 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en.png new file mode 100644 index 0000000000..cd9d503167 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4df538763a96fb5511bbb175856943539951b804b7ca2b5d69d7033fa5d3588 +size 11012 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en.png new file mode 100644 index 0000000000..3663e98d52 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f07736b16ece457794cdbd3859ede26ed0aa4b53acdc69d7f913df9a9b0fad1f +size 16002 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en.png new file mode 100644 index 0000000000..244e0ce786 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ad16996462532092686f1638f3ee1f40ede99b5ccb2bc53e57931f50920e62b5 +size 12629 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en.png new file mode 100644 index 0000000000..6eedc6c22c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:05368c1bf4caed7d565daadade2dade7046fce15f1bd37eff4b1ec0faed5e948 +size 9163 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en.png new file mode 100644 index 0000000000..5085864cc6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:690df48431acad687f2a2e72aebba4fa63de433ed68b7b9e19818a78973cd211 +size 22676 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en.png new file mode 100644 index 0000000000..1e85e954f6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34073398379389aa862c892e6eaa33574606071597a0095dfa5c84b99154b5df +size 17218 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en.png new file mode 100644 index 0000000000..252a083540 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1dcdb9795595067c90efd2c3e3d34d740b5c1ed5e02d487c7e03f10f9653c3a7 +size 13134 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en.png new file mode 100644 index 0000000000..73e31905c7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cef17e2d54dd9c8eacb9e7d3206fabf9acc91f1d4192bd9e13da0d9356eb536b +size 32669 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en.png new file mode 100644 index 0000000000..5a01e301a2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2b55a103be0ad883d0aaf31cceac1a5b33f1923dc4d96778c261ce98e5b0d435 +size 37334 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en.png new file mode 100644 index 0000000000..3e5373a5b6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59a2703c34c580655ff8e8c4681ad3937d7daaf9211e899ac8ade2826fdaab03 +size 10782 diff --git a/tests/uitests/src/test/snapshots/images/libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en.png new file mode 100644 index 0000000000..c77ea23f32 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:692bf4dd932e39d3b3d7c2b234524b9e31a288808b1a5c4574e3a0ca19d6a725 +size 23296 diff --git a/tests/uitests/src/test/snapshots/images/libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en.png new file mode 100644 index 0000000000..616133a868 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:303c3a69b1ed1bcb8bf2253b8a70f2a8c06171e583f43dbc10023d66880c9e6d +size 21309 diff --git a/tests/uitests/src/test/snapshots/images/libraries.ui.common.nodes_EmptyView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.ui.common.nodes_EmptyView_Day_0_en.png new file mode 100644 index 0000000000..1b6fb4bab8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.ui.common.nodes_EmptyView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96a867cb12498cbdc97957bee07855dfaa13602baddaf933aff2b666ef4c7650 +size 3642 diff --git a/tests/uitests/src/test/snapshots/images/libraries.ui.common.nodes_EmptyView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.ui.common.nodes_EmptyView_Night_0_en.png new file mode 100644 index 0000000000..d6fd8eeb70 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.ui.common.nodes_EmptyView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5bb36ccd718f3fec5b04f1bc812dc7718b5ea7fa4619c8b031466297a8d016fd +size 3659 diff --git a/tools/compound/addAutoMirrored.py b/tools/compound/addAutoMirrored.py new file mode 100644 index 0000000000..a8e22c42a5 --- /dev/null +++ b/tools/compound/addAutoMirrored.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 + +files = [ + "ic_compound_arrow_left.xml", + "ic_compound_arrow_right.xml", + "ic_compound_arrow_up_right.xml", + "ic_compound_attachment.xml", + "ic_compound_block.xml", + "ic_compound_chart.xml", + "ic_compound_chat.xml", + "ic_compound_chat_new.xml", + "ic_compound_chat_problem.xml", + "ic_compound_chat_solid.xml", + "ic_compound_chevron_left.xml", + "ic_compound_chevron_right.xml", + "ic_compound_cloud.xml", + "ic_compound_cloud_solid.xml", + "ic_compound_collapse.xml", + "ic_compound_company.xml", + "ic_compound_compose.xml", + "ic_compound_copy.xml", + "ic_compound_dark_mode.xml", + "ic_compound_devices.xml", + "ic_compound_document.xml", + "ic_compound_earpiece.xml", + "ic_compound_edit.xml", + "ic_compound_edit_solid.xml", + "ic_compound_expand.xml", + "ic_compound_extensions.xml", + "ic_compound_extensions_solid.xml", + "ic_compound_file_error.xml", + "ic_compound_files.xml", + "ic_compound_forward.xml", + "ic_compound_guest.xml", + "ic_compound_history.xml", + "ic_compound_host.xml", + "ic_compound_image.xml", + "ic_compound_image_error.xml", + "ic_compound_indent_decrease.xml", + "ic_compound_indent_increase.xml", + "ic_compound_italic.xml", + "ic_compound_key.xml", + "ic_compound_key_off.xml", + "ic_compound_key_off_solid.xml", + "ic_compound_key_solid.xml", + "ic_compound_leave.xml", + "ic_compound_link.xml", + "ic_compound_list_bulleted.xml", + "ic_compound_lock_off.xml", + "ic_compound_mark_as_unread.xml", + "ic_compound_mark_threads_as_read.xml", + "ic_compound_marker_read_receipts.xml", + "ic_compound_mic_off.xml", + "ic_compound_mic_off_solid.xml", + "ic_compound_notifications_off.xml", + "ic_compound_notifications_off_solid.xml", + "ic_compound_offline.xml", + "ic_compound_play.xml", + "ic_compound_play_solid.xml", + "ic_compound_polls.xml", + "ic_compound_polls_end.xml", + "ic_compound_pop_out.xml", + "ic_compound_qr_code.xml", + "ic_compound_quote.xml", + "ic_compound_reaction_add.xml", + "ic_compound_reply.xml", + "ic_compound_restart.xml", + "ic_compound_room.xml", + "ic_compound_search.xml", + "ic_compound_send.xml", + "ic_compound_send_solid.xml", + "ic_compound_share_android.xml", + "ic_compound_sidebar.xml", + "ic_compound_sign_out.xml", + "ic_compound_spinner.xml", + "ic_compound_spotlight.xml", + "ic_compound_switch_camera_solid.xml", + "ic_compound_threads.xml", + "ic_compound_threads_solid.xml", + "ic_compound_unknown.xml", + "ic_compound_unknown_solid.xml", + "ic_compound_unpin.xml", + "ic_compound_user_add.xml", + "ic_compound_user_add_solid.xml", + "ic_compound_video_call.xml", + "ic_compound_video_call_declined_solid.xml", + "ic_compound_video_call_missed_solid.xml", + "ic_compound_video_call_off.xml", + "ic_compound_video_call_off_solid.xml", + "ic_compound_video_call_solid.xml", + "ic_compound_visibility_off.xml", + "ic_compound_voice_call.xml", + "ic_compound_voice_call_solid.xml", + "ic_compound_volume_off.xml", + "ic_compound_volume_off_solid.xml", + "ic_compound_volume_on.xml", + "ic_compound_volume_on_solid.xml", +] + + +def main(): + for file in files: + # Open file for read + with open("./libraries/compound/src/main/res/drawable/" + file, 'r') as f: + data = f.read().split("\n") + # Open file to write + with open("./libraries/compound/src/main/res/drawable/" + file, 'w') as f: + # Write new data + # write the 3 first lines in data + for i in range(3): + f.write(data[i] + "\n") + f.write(" android:autoMirrored=\"true\"\n") + # write the rest of the data + for i in range(3, len(data) - 1): + f.write(data[i] + "\n") + print("Added autoMirrored to " + str(len(files)) + " files.") + + +if __name__ == "__main__": + main() diff --git a/tools/compound/import_tokens.sh b/tools/compound/import_tokens.sh new file mode 100755 index 0000000000..700decf81e --- /dev/null +++ b/tools/compound/import_tokens.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +set -e + +BRANCH='main' + +while getopts b: flag +do + case "${flag}" in + b) BRANCH=${OPTARG};; + *) echo "usage: $0 [-b branch]" >&2 + exit 1 ;; + esac +done + +echo "Branch used: $BRANCH" + +echo "Cloning the compound-design-tokens repository..." +if [ -d tmpCompound ]; then + echo "Deleting tmpCompound folder..." + rm -rf tmpCompound +fi +mkdir tmpCompound +pushd tmpCompound +git clone --branch "${BRANCH}" https://github.com/vector-im/compound-design-tokens + +echo "Copying files from tokens repository..." +rm -R ../libraries/compound/src/main/res/drawable +cp -R compound-design-tokens/assets/android/res/drawable ../libraries/compound/src/main/res/ +cp -R compound-design-tokens/assets/android/src/* ../libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/ +popd + +echo "Adding autoMirrored attribute..." +python3 ./tools/compound/addAutoMirrored.py + +echo "Removing temporary files..." +rm -rf tmpCompound + +echo "Done!" diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 4264b35229..82c273db2e 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -22,6 +22,12 @@ "settings_rageshake.*" ] }, + { + "name" : ":features:announcement:impl", + "includeRegex" : [ + "screen\\.space_announcement\\..*" + ] + }, { "name" : ":features:logout:impl", "includeRegex" : [ @@ -179,6 +185,7 @@ "confirm_recovery_key_banner_.*", "banner\\.set_up_recovery\\..*", "banner\\.battery_optimization\\..*", + "banner\\.new_sound\\..*", "full_screen_intent_banner_.*", "screen_migration_.*", "screen_invites_.*" @@ -200,6 +207,12 @@ "screen\\.security_and_privacy\\..*" ] }, + { + "name" : ":features:space:impl", + "includeRegex" : [ + "screen\\.leave_space\\..*" + ] + }, { "name" : ":features:userprofile:shared", "includeRegex" : [ @@ -287,7 +300,8 @@ "screen_notification_settings_.*", "screen_blocked_users_.*", "full_screen_intent_banner_.*", - "troubleshoot_notifications_entry_point_.*" + "troubleshoot_notifications_entry_point_.*", + "screen\\.labs\\..*" ] }, { diff --git a/tools/templates/files/fileTemplates/Template Module Feature Node Flow Impl.kt b/tools/templates/files/fileTemplates/Template Module Feature Node Flow Impl.kt index 8f2f46ba87..2c23326e0f 100644 --- a/tools/templates/files/fileTemplates/Template Module Feature Node Flow Impl.kt +++ b/tools/templates/files/fileTemplates/Template Module Feature Node Flow Impl.kt @@ -10,7 +10,7 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler @@ -20,7 +20,7 @@ import kotlinx.parcelize.Parcelize // CHANGE THE SCOPE @ContributesNode(AppScope::class) -@Inject +@AssistedInject class ${FEATURE_NAME}FlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.1.kt b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.1.kt index 50f4029f4c..5bd0f4fdc4 100644 --- a/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.1.kt +++ b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.1.kt @@ -6,13 +6,13 @@ 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.Assisted -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import dev.zacsweers.metro.AppScope // CHANGE THE SCOPE @ContributesNode(AppScope::class) -@Inject +@AssistedInject class ${NAME}Node( @Assisted buildContext: BuildContext, @Assisted plugins: List,